BZOJ5110 : [CodePlus2017]Yazid 的新生舞会
显然每个区间最多只有一种绝对众数,故每个数值独立,考虑枚举每种数值作为绝对众数然后计算贡献。
设$s_i$表示前$i$个中该数值的出现次数,则要选择一对下标$l,r$满足:
- $0\leq l<r\leq n$。
- $2s_r-r>2s_l-l$。
根据数字出现位置,假设它出现了$k$次,则可以将序列划分成$k+1$段递减的等差数列,显然同一段等差数列之间不会有任何贡献。
那么从左往右枚举每一段,用树状数组维护每种$2s_i-i$的出现次数即可。
时间复杂度$O(n\log n)$。
#include<cstdio> typedef long long ll; const int N=500010,M=N<<1,BUF=N*10; int n,lim,m,i,j,x,g[N],nxt[N],q[N];ll fa[M],fb[M],fc[M],ans;char Buf[BUF],*buf=Buf; inline void read(int&a){for(a=0;*buf<48;buf++);while(*buf>47)a=a*10+*buf++-48;} inline void add(int x,int p){ ll y=1LL*x*x-3*x+2; for(int i=x;i<=lim;i+=i&-i)fa[i]+=p,fb[i]+=x*p,fc[i]+=y*p; } inline ll ask(int x){ ll a=0,b=0,c=0; for(int i=x;i>0;i-=i&-i)a+=fa[i],b+=fb[i],c+=fc[i]; return a*(1LL*x*x+3*x)-b*2*x+c; } inline void work(int l,int r,int v,int p){ r-=l; l=v-r+n+1; r=v+n+1; if(p)add(l,-1),add(r+1,1);else ans+=ask(r-1)-ask(l-2),add(l,1),add(r+1,-1); } int main(){ fread(Buf,1,BUF,stdin);read(n),read(i); lim=n*2+1; for(i=1;i<=n;i++)read(x),nxt[i]=g[x],g[x]=i; q[0]=n+1; for(i=0;i<=n;i++)if(g[i]){ for(m=0,j=g[i];j;j=nxt[j])q[++m]=j; q[m+1]=0; for(j=m+1;j;j--)work(q[j],q[j-1]-1,(m-j+1)*2-q[j],0); for(j=m+1;j;j--)work(q[j],q[j-1]-1,(m-j+1)*2-q[j],1); } return printf("%lld",ans/2),0; }