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;
}

  

posted @ 2017-11-27 21:02 Claris 阅读(...) 评论(...) 编辑 收藏