P4062 [Code+#1]Yazid 的新生舞会
杭电多校懂得都懂
Code1
分治
比较喜欢分治的做法,非常好写。skylee大佬题解
首先对于任何一个区间来说,由于两个端点不确定性非常难以一次性统计多组区间,因为它们没有相似之处。
考虑分治,花费log\loglog的代价使得当前考虑的区间必须经过mid\text{mid}mid,意味着当前区间左端点必须在[l,mid][\text{l},\text{mid}][l,mid]区间内部,而右端点必须在[mid+1,r][\text{mid}+1,\text{r}][mid+1,r]区间内部,这样的区间特殊在一定经过mid使得可以先预处理左端点的一些信息,然后枚举右端点一次性统计多个区间。
首先枚举可能作为区间绝对众数的数v\text vv,然后考虑哪些上述区间能使得该数作为区间的绝对众数。
vl,vr\text{vl},\text{vr}vl,vr分别是可能使v\text vv作为区间[vl,vr][\text{vl},\text{vr}][vl,vr]的绝对众数。
cntvl\text{cnt}_{\text {vl}}cntvl:vl→mid\text{vl}\to \text{mid}vl→mid 数字v\text vv出现的次数。
cntvr\text{cnt}_{\text {vr}}cntvr:mid+1→vr\text{mid}+1\to \text {vr}mid+1→vr 数字v\text vv出现的次数。
cntvr+cntvl>12[r−l+1]\text{cnt}_{\text {vr}}+\text{cnt}_{\text {vl}}>\frac{1}{2}[r-l+1]cntvr+cntvl>21[r−l+1]
从上面式子可以得出
2cntvl+l−1>r−2cntvr2\text{cnt}_{\text {vl}}+l-1>r-2\text{cnt}_{\text {vr}}2cntvl+l−1>r−2cntvr
考虑枚举vr\text{vr}vr,我们只需要统计有哪些左端点vl\text{vl}vl满足上面式子即可,显然可以预处理+前缀和一次性统计。
还有一个问题就是哪些数可能是区间的绝对众数?
如果一个数vvv是区间[vl,vr][\text{vl},\text{vr}][vl,vr]的绝对众数,那么它一定是区间[vl,mid][\text{vl},\text{mid}][vl,mid]以及[mid+1,vr][\text{mid}+1,\text{vr}][mid+1,vr]的绝对众数,于是可以提前预处理。
显然能够满足上述条件作为区间绝对众数的种类不会很多。上面大佬题解说是log\loglog量级的。
时间复杂度O(nlog2n)O(n\log ^2n)O(nlog2n)
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
template <class T=int> T rd()
{T res=0;T fg=1;char ch=getchar();while(!isdigit(ch)) {if(ch=='-') fg=-1;ch=getchar();}while( isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=getchar();return res*fg;
}
const int N=500010;
int a[N],n;
ll ans;
int b[N],cnt;
int mp[N],in[N];
int num[2*N];
void solve(int l,int r)
{if(l==r) return ans++,void();int mid=l+r>>1;solve(l,mid),solve(mid+1,r);cnt=0;for(int i=mid;i>=l;i--){mp[a[i]]++;if(mp[a[i]]>(mid-i+1)/2){if(in[a[i]]) continue;in[a[i]]=1;b[++cnt]=a[i];}}for(int i=l;i<=mid;i++) mp[a[i]]--;for(int i=mid+1;i<=r;i++){mp[a[i]]++;if(mp[a[i]]>(i-mid)/2){if(in[a[i]]) continue;in[a[i]]=1;b[++cnt]=a[i];}}for(int i=mid+1;i<=r;i++) mp[a[i]]--;for(int i=l;i<=r;i++) in[a[i]]=0;//for(int i=1;i<=cnt;i++) cout<<b[i]<<" \n"[i==cnt];for(int i=1;i<=cnt;i++){int cur=0;int L=3*n,R=0;for(int j=mid;j>=l;j--){if(a[j]==b[i]) cur++;num[2*cur+j-1]++;L=min(L,2*cur+j-1);R=max(R,2*cur+j-1);}for(int i=R;i>L;i--) num[i-1]+=num[i];cur=0;for(int j=mid+1;j<=r;j++){if(a[j]==b[i]) cur++;ans+=(num[max(L,j-2*cur+1)]);}for(int i=R;i>=L;i--) num[i]=0;}}
int main()
{n=rd();rd();for(int i=1;i<=n;i++) a[i]=rd();solve(1,n);printf("%lld\n",ans);return 0;
}
Code2
OMG_wc大佬题解
Zechariah大佬题解
其实赛时的将此问题转化成了下面的问题:如何求小于一段公差为1的等差数列的个数?
- 求在a1→na_{1\to n}a1→n中求小于xxx的个数,小于x+1x+1x+1的个数,小于x+2x+2x+2的个数,小于x+kx+kx+k的个数把他们累加。
然后就死了???这不就是前缀和然后区间询问吗?wtcl
开个桶,然后做一个前缀和,然后就是个区间询问[x,x+k][x,x+k][x,x+k]的问题。。。
此题首先记录每个数出现的位置,单独考虑每个数作为区间的绝对众数。
不妨设当前考虑的数为v\text vv
v\text vv能作为区间(L,R](\text L,\text R](L,R]的众数的充要条件是
cntR−cntL>R−L2\text{cnt}_{\text R}-\text{cnt}_{\text L}>\frac {\text R-\text L}{2}cntR−cntL>2R−L
即
2cntL−L<2cntR−R,L<R2\text{cnt}_{\text L}-\text L<2\text{cnt}_{\text R}-\text R ,\text{L}<\text R 2cntL−L<2cntR−R,L<R
记bL=2cntL−L\text b_{\text L}=2\text{cnt}_{\text L}-\text LbL=2cntL−L
于是转化成二维偏序:
L<RbL<bR\text{L}<\text R \\ \text b_{\text L}<\text b_{\text R}L<RbL<bR
显然我们枚举右端点,用个树状数组就可以统计,但是复杂度不行。
观察每个数出现的位置将整个区间划分为下面模式
[1,…)[…)[…)[…,n+1)\color{blue}[1,\dots)[\dots)\color{red}[\dots)\color{black}[\dots,\text{n+1}) [1,…)[…)[…)[…,n+1)
上面)))代表的就是该数出现的位置。
对于每个[…)\color{red}[\dots)[…)内部bi\text b_{\text i}bi是单调下降,且公差为1,显然当区间右端点在此区间内部时,区间左端点不可能在其内部。换句话说就是只有前面的即[1,…)[…)\color{blue}[1,\dots)[\dots)[1,…)[…)可能对区间右端点在[…)\color{red}[\dots)[…)产生贡献。
而且重要的是[…)\color{red}[\dots)[…)的bj\text b_{\text j}bj连续的,即对于每一个[…)\color{red}[\dots)[…)的bj\text b_{\text j}bj我们需要在[1,…)[…)\color{blue}[1,\dots)[\dots)[1,…)[…)找到bi<bj\text b_{\text i}<\text b_{\text j}bi<bj,显然就是最开始那个问题,只需要求个前缀和然后区间询问即可。
每次过考虑[…)\color{red}[\dots)[…)后进行区间修改,将[…)\color{red}[\dots)[…)内部的bj\text b_{\text j}bj插入数据结构中。
区间修改+前缀和区间询问
树状数组的话可以把差分转化为单点修改,那么本次要做到区间询问就意味着要用树状数组维护三维前缀和
∑i=1n∑j=1i∑k=1jdk=12[(n2+3n+2)∑i=1ndi−(2n+3)∑i=1ni⋅di+∑i=1ni2⋅di]\sum_{i=1}^{n}\sum_{j=1}^i\sum_{k=1}^jd_k=\frac{1}{2}[(n^2+3n+2)\sum_{i=1}^nd_i-(2n+3)\sum_{i=1}^ni·d_i+\sum_{i=1}^ni^2·d_i] i=1∑nj=1∑ik=1∑jdk=21[(n2+3n+2)i=1∑ndi−(2n+3)i=1∑ni⋅di+i=1∑ni2⋅di]
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
template <class T=int> T rd()
{T res=0;T fg=1;char ch=getchar();while(!isdigit(ch)) {if(ch=='-') fg=-1;ch=getchar();}while( isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=getchar();return res*fg;
}
const int N=500010;
int a[N],n;
vector<int> loc[N];
ll c0[N<<1],c1[N<<1],c2[N<<1];
void update(int k,ll v,int n)
{ll i=k*v,ii=1ll*k*k*v;while(k<=n){c0[k]+=v;c1[k]+=i;c2[k]+=ii;k+=k&-k;}
}
ll qsum(int k)
{ll ans=0;ll k1=1ll*k*k+3*k+2,k2=2*k+3;while(k){ans+=k1*c0[k]-k2*c1[k]+c2[k];k-=k&-k;}return ans>>1;
}
int main()
{n=rd();rd();for(int i=1;i<=n;i++) a[i]=rd(),loc[a[i]].push_back(i);ll ans=0;const int Bs=n+1;for(int i=0;i<n;i++){if(loc[i].empty()) continue;int pre=0;loc[i].push_back(n+1);for(int j=0;j<loc[i].size();j++){int R=2*j-pre+Bs,L=2*j-(loc[i][j]-1)+Bs;ans+=qsum(R-1)-qsum(max(0,L-2));// 严格小于update(L,1,n<<1|1);update(R+1,-1,n<<1|1);pre=loc[i][j];}pre=0;for(int j=0;j<loc[i].size();j++){int R=2*j-pre+Bs,L=2*j-(loc[i][j]-1)+Bs;update(L,-1,n<<1|1);update(R+1,+1,n<<1|1);pre=loc[i][j];}}printf("%lld\n",ans);return 0;
}
要加油哦~