# 【BZOJ3771】 Triple

## Triple

#### Solution

FFT+容斥

1、只选一个，此时答案显然就是$a[i]$

2、选两个，此时不去重答案是$a[i]^2$，但是其中会有两个一样的情况，然后去掉顺序，所以最终答案是$(a[i]^2-b[i]) \over 2$

3、选三个，此时不去重答案是$a[i]^3$，去掉(x,x,x)与(x,y,y)、(y,x,y)、(y,y,x)的情况，就要减掉$a[i] \times b[i] \times 3$，但是这样多减去了两个(x,x,x)，所以要加回来$c[i] \times 2$

code:

#include<bits/stdc++.h>
using namespace std;
struct comp{
double x,y;
comp (double xx=0,double yy=0){
x=xx,y=yy;
}
}a[200010],b[200010],c[200010],ans[200010];
comp operator +(comp a,comp b){return comp(a.x+b.x,a.y+b.y);}
comp operator -(comp a,comp b){return comp(a.x-b.x,a.y-b.y);}
comp operator *(comp a,comp b){return comp(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);}
comp operator *(comp a,double b){return comp(a.x*b,a.y*b);}
int rev[200010];
int lim=1;
const double pi=acos(-1);
void fft(comp *A,int type){
for(int i=0;i<lim;++i){
if(i<rev[i])swap(A[i],A[rev[i]]);
}
for(int mid=1;mid<lim;mid<<=1){
comp wn=comp(cos(pi/mid),type*sin(pi/mid));
for(int j=0,R=mid<<1;j<lim;j+=R){
comp w=comp(1,0);
for(int k=0;k<mid;++k,w=w*wn){
comp x=A[j+k],y=w*A[j+k+mid];
A[j+k]=x+y;
A[j+k+mid]=x-y;
}
}
}
if(type==-1){
for(int i=0;i<lim;++i)A[i].x/=lim;
}
}
int main(){
int n;
scanf("%d",&n);
int len=0;
for(int i=1;i<=n;++i){
int x;
scanf("%d",&x);
a[x].x+=1.0,b[x*2].x+=1.0,c[x*3].x+=1.0;
len=max(len,x*3);
}
int tmp=0;
while(lim<=len){
lim<<=1,tmp++;
}
//cout<<lim<<" "<<tmp<<endl;
for(int i=0;i<=lim;++i){
rev[i]=rev[i>>1]>>1|(i&1)<<(tmp-1);
}
fft(a,1);
fft(b,1);
fft(c,1);
for(int i=0;i<lim;++i){
ans[i]=a[i];
ans[i]=ans[i]+(a[i]*a[i]-b[i])*0.5;
ans[i]=ans[i]+(a[i]*a[i]*a[i]-b[i]*a[i]*3.0+c[i]*2.0)*(1.0/6.0);
}
fft(ans,-1);
for(int i=0;i<lim;++i){
int num=ans[i].x+0.5;
if(num){
printf("%d %d\n",i,num);
}
}
}

posted @ 2019-09-30 16:00  FakeDragon  阅读(133)  评论(0编辑  收藏  举报