您的位置:首页 > 其它

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

2017-08-15 15:45 537 查看
传送门

%%%度神

很强的计数题

题目就是求形如ABAB的个数,发现这个很难求,补集转换一下,答案就是总数减去AABB和ABBA的个数

求总数很简单,就是

∏i=1n(ai2)

ai是第i中颜色个数

AABB的个数可以枚举p,然后用颜色的前后缀和求出来。

重点就是求ABBA了

直接求还是不好求,可以设一个阈值 S

Big为个数S的颜色的集合,Small为个数小于等于S的颜色集合。

那么就有四种情况:

A∈Big,B∈Big

A∈Big,B∈Small

A∈Small,B∈Big

A∈Small,B∈Small

分类讨论

A∈Small,B∈Small

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

A∈Big,B∈Small

大颜色包含小颜色,枚举每种小颜色x和大颜色y,枚举左端点,右端点可以求出答案∑i=2ax∑j=1i−1prey,j×(ay−prey,i)∑i=2ax(ay−prey,i)∑j=1i−1prey,j维护一下∑i−1j=1prey,j,就不需要枚举右端点了,整个复杂度就是O(n2S)

A∈Small,B∈Big和A∈Small,B∈Big

这两类情况的解法一样,枚举A的左端点和右端点,枚举B的种类,答案是∑i=2aA∑j=1i−1(preB,i−preB,j2)展开可以得到12∑i=2aA(pre2B,i×(i−1)−2×preB,i∑j=1i−1preB,j+∑j=1i−1pre2B,j+∑j=1i−1preB,j+preB,i×(i−1))同样维护∑i−1j=1preB,j,∑i−1j=1pre2B,j就可以不用枚举右端点,复杂度就是O(n2S)

这样就可以了理论上S取nlogn−−−−√可以达到最优复杂度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
,pre
,lst
,Size
,bit
;
int pr[M]
,p
[M],q
[M],p2
[M];
vector<int> c
,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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: