[计数 补集转换][阈值] Codechef SEAARC.Sereja and Arcs

来源:互联网 发布:淘宝女装背景 编辑:程序博客网 时间:2024/06/04 00:27

传送门
%%%度神
很强的计数题
题目就是求形如ABAB的个数,发现这个很难求,补集转换一下,答案就是总数减去AABB和ABBA的个数
求总数很简单,就是

i=1n(ai2)

ai是第i中颜色个数
AABB的个数可以枚举p,然后用颜色的前后缀和求出来。
重点就是求ABBA了
直接求还是不好求,可以设一个阈值 S
Big为个数S的颜色的集合,Small为个数小于等于S的颜色集合。
那么就有四种情况:

  • ABigBBig
  • ABigBSmall
  • ASmallBBig
  • ASmallBSmall

分类讨论

  • ASmallBSmall

    这个子问题是小颜色包含小颜色,这个子问题就是典型的二维数点,可以证明iX(ai2)O(nS)级别的,也就是这些颜色的数对和在nS以内,那么就用离线的求二维数点的方案就可以了。复杂度加个树状数组的log,就是O(nSlogn)

  • ABigBSmall
    大颜色包含小颜色,枚举每种小颜色x和大颜色y,枚举左端点,右端点可以求出答案

    i=2axj=1i1prey,j×(ayprey,i)
    i=2ax(ayprey,i)j=1i1prey,j
    维护一下i1j=1prey,j,就不需要枚举右端点了,整个复杂度就是O(n2S)

  • ASmallBBigASmallBBig
    这两类情况的解法一样,枚举A的左端点和右端点,枚举B的种类,答案是

    i=2aAj=1i1(preB,ipreB,j2)
    展开可以得到
    12i=2aA(pre2B,i×(i1)2×preB,ij=1i1preB,j+j=1i1pre2B,j+j=1i1preB,j+preB,i×(i1))
    同样维护i1j=1preB,ji1j=1pre2B,j就可以不用枚举右端点,复杂度就是O(n2S)

这样就可以了理论上Snlogn可以达到最优复杂度O(bnlogn),但是树状数组的常数贼小,而O(n2S)部分常数很大,所以取S=n跑的最快。

打了一个晚上结果不小心关了电脑,代码就不见了…
第二天10分钟又打了一遍…

#include <cstdio>#include <iostream>#include <algorithm>#include <cmath>#include <vector>#include <cstring>#include <string>using namespace std;const int N=100010,M=330,P=1e9+7,inv2=P+1>>1;inline char nc(){    static char buf[100000],*p1=buf,*p2=buf;    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;}inline void rea(int &x){    char c=nc(); x=0;    for(;c>'9'||c<'0';c=nc());for(;c>='0'&&c<='9';x=x*10+c-'0',c=nc());}inline void add(int &x,int y){    if((x+=y)>=P) x-=P;}int n,S,ans,a[N],pre[N],lst[N],Size[N],bit[N];int pr[M][N],p[N][M],q[N][M],p2[N][M];vector<int> c[N],bg;inline int C(int x){    return 1LL*x*(x-1)/2%P;}inline int Query(int l,int r){    int ret=0;    for(;l<=n;l+=l&-l) ret+=bit[l];    for(r++;r<=n;r+=r&-r) ret-=bit[r];    return ret;}inline void Add(int x){    for(;x;x-=x&-x) bit[x]++;}int main(){    freopen("arcs.in","r",stdin);    freopen("arcs.out","w",stdout);    rea(n); S=sqrt(n);    for(int i=1;i<=n;i++)        rea(a[i]),c[a[i]].push_back(i);     for(int i=1;i<=n;i++)        pre[i]=lst[a[i]],lst[a[i]]=i;    int cur=0;    for(int i=1;i<=100000;i++){        if(c[i].size()>S) bg.push_back(i);        add(ans,1LL*cur*C(c[i].size())%P),add(cur,C(c[i].size()));    }    for(int i=1;i<=n;i++){        add(cur,(P-(c[a[i]].size()-Size[a[i]]-1))%P);        add(ans,P-1LL*Size[a[i]]*(cur+P-C(c[a[i]].size()-Size[a[i]]-1))%P);        Size[a[i]]++;    }    memset(Size,0,sizeof(Size));    for(int i=1;i<=n;i++){        for(int j=0;j<bg.size();j++)            pr[j][i]=pr[j][i-1]+(a[i]==bg[j]);    }    for(int i=1;i<=n;i++){        if(c[a[i]].size()<=S){            for(int j=0;c[a[i]][j]<i;j++)                add(ans,(P-Query(c[a[i]][j]+1,i)+C(Size[a[i]]-j-1))%P),Add(c[a[i]][j]);            for(int j=0;j<bg.size();j++){                if(bg[j]==a[i]) continue;                q[i][j]=(q[pre[i]][j]+pr[j][pre[i]])%P;                add(ans,P-1LL*(c[bg[j]].size()-Size[bg[j]])*q[i][j]%P);            }        }        for(int j=0;j<bg.size();j++){            if(bg[j]==a[i]) continue;            p[i][j]=(p[pre[i]][j]+pr[j][pre[i]])%P;            p2[i][j]=(1LL*pr[j][pre[i]]*pr[j][pre[i]]+p2[pre[i]][j])%P;            add(ans,P-1LL*inv2*((1LL*Size[a[i]]*pr[j][i]%P*pr[j][i]%P+P-2LL*pr[j][i]*p[i][j]%P+p2[i][j]+p[i][j]+P-1LL*Size[a[i]]*pr[j][i]%P)%P)%P);        }        Size[a[i]]++;    }    printf("%d\n",ans);    return 0;}
原创粉丝点击