Bzoj3771 Triple
Submit: 563 Solved: 317
Description
我们讲一个悲伤的故事。
从前有一个贫穷的樵夫在河边砍柴。
这时候河里出现了一个水神,夺过了他的斧头,说:
“这把斧头,是不是你的?”
樵夫一看:“是啊是啊!”
水神把斧头扔在一边,又拿起一个东西问:
“这把斧头,是不是你的?”
樵夫看不清楚,但又怕真的是自己的斧头,只好又答:“是啊是啊!”
水神又把手上的东西扔在一边,拿起第三个东西问:
“这把斧头,是不是你的?”
樵夫还是看不清楚,但是他觉得再这样下去他就没法砍柴了。
于是他又一次答:“是啊是啊!真的是!”
水神看着他,哈哈大笑道:
“你看看你现在的样子,真是丑陋!”
之后就消失了。
樵夫觉得很坑爹,他今天不仅没有砍到柴,还丢了一把斧头给那个水神。
于是他准备回家换一把斧头。
回家之后他才发现真正坑爹的事情才刚开始。
水神拿着的的确是他的斧头。
但是不一定是他拿出去的那把,还有可能是水神不知道怎么偷偷从他家里拿走的。
换句话说,水神可能拿走了他的一把,两把或者三把斧头。
樵夫觉得今天真是倒霉透了,但不管怎么样日子还得过。
他想统计他的损失。
樵夫的每一把斧头都有一个价值,不同斧头的价值不同。总损失就是丢掉的斧头价值和。
他想对于每个可能的总损失,计算有几种可能的方案。
注意:如果水神拿走了两把斧头a和b,(a,b)和(b,a)视为一种方案。拿走三把斧头时,(a,b,c),(b,c,a),(c,a,b),(c,b,a),(b,a,c),(a,c,b)视为一种方案。
Input
第一行是整数N,表示有N把斧头。
接下来n行升序输入N个数字Ai,表示每把斧头的价值。
Output
若干行,按升序对于所有可能的总损失输出一行x y,x为损失值,y为方案数。
Sample Input
4
4
5
6
7
4
5
6
7
Sample Output
4 1
5 1
6 1
7 1
9 1
10 1
11 2
12 1
13 1
15 1
16 1
17 1
18 1
样例解释
11有两种方案是4+7和5+6,其他损失值都有唯一方案,例如4=4,5=5,10=4+6,18=5+6+7.
5 1
6 1
7 1
9 1
10 1
11 2
12 1
13 1
15 1
16 1
17 1
18 1
样例解释
11有两种方案是4+7和5+6,其他损失值都有唯一方案,例如4=4,5=5,10=4+6,18=5+6+7.
HINT
所有数据满足:Ai<=40000
Source
数学问题 生成函数 FFT 容斥
题面挺带感的233
设x[]为只拿一把斧头的方案构建的指数生成函数,y[]为拿了相同两把斧头的方案构建的指数生成函数,z[]为拿了相同三把斧头的方案构建的指数生成函数。
那么可以容斥计算:
拿一把的方案数:$O(n)$直接统计
拿两把的方案数:$ans=(x*x-y)/2$
拿三把的方案数: $ans= (x*x*x-3*x*y+2*z)/6 $
1 /*by SilverN*/ 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<cstdio> 6 #include<cmath> 7 #include<vector> 8 #define LL long long 9 using namespace std; 10 const double pi=acos(-1.0); 11 const int mxn=160010; 12 int read(){ 13 int x=0,f=1;char ch=getchar(); 14 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 15 while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();} 16 return x*f; 17 } 18 struct com{ 19 double x,y; 20 com operator + (com b){return (com){x+b.x,y+b.y};} 21 com operator - (com b){return (com){x-b.x,y-b.y};} 22 com operator * (com b){return (com){x*b.x-y*b.y,x*b.y+y*b.x};} 23 com operator / (double v){return (com){x/v,y/v};} 24 }a[mxn],b[mxn],c[mxn]; 25 int N,l,rev[mxn]; 26 void FFT(com *a,int flag){ 27 for(int i=0;i<N;i++)if(i<rev[i])swap(a[i],a[rev[i]]); 28 for(int i=1;i<N;i<<=1){ 29 com wn=(com){cos(pi/i),flag*sin(pi/i)}; 30 for(int j=0;j<N;j+=i<<1){ 31 com w=(com){1,0}; 32 for(int k=0;k<i;k++,w=w*wn){ 33 com x=a[k+j],y=w*a[k+j+i]; 34 a[k+j]=x+y; 35 a[k+j+i]=x-y; 36 } 37 } 38 } 39 if(flag==-1)for(int i=0;i<N;i++) a[i].x/=N; 40 return; 41 } 42 int n,w[mxn]; 43 LL ans[mxn],d[mxn]; 44 int main(){ 45 int i,j,mx=0; 46 n=read(); 47 for(i=1;i<=n;i++)w[i]=read(),mx=max(mx,w[i]); 48 int m=mx*3; 49 for(N=1;N<m;N<<=1)l++; 50 for(i=0;i<=N;i++) 51 rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1)); 52 for(i=1;i<=n;i++){++ans[w[i]];}//只拿一把 53 // 54 for(i=1;i<=n;i++)a[w[i]].x+=1; 55 FFT(a,1); 56 for(i=0;i<=N;i++) 57 a[i]=a[i]*a[i]; 58 FFT(a,-1); 59 for(i=0;i<=N;i++)d[i]+=(int)(a[i].x+0.3); 60 for(i=1;i<=n;i++)d[w[i]+w[i]]--; 61 for(i=0;i<=N;i++)ans[i]+=d[i]/2; 62 //拿两把 63 memset(a,0,sizeof a); 64 for(i=1;i<=n;i++)a[w[i]].x++; 65 FFT(a,1); 66 for(i=0;i<=N;i++) a[i]=a[i]*a[i]*a[i]; 67 FFT(a,-1); 68 for(i=0;i<=N;i++)d[i]=(int)(a[i].x+0.3); 69 memset(a,0,sizeof a); 70 for(i=1;i<=n;i++){ 71 a[w[i]].x+=1; 72 b[w[i]+w[i]].x+=1; 73 } 74 FFT(a,1);FFT(b,1); 75 for(i=0;i<=N;i++)c[i]=a[i]*b[i]; 76 FFT(c,-1); 77 for(i=0;i<=N;i++)d[i]-=((int)(c[i].x+0.3))*3; 78 for(i=1;i<=n;i++){d[w[i]*3]+=2;} 79 for(i=0;i<=N;i++)ans[i]+=d[i]/6; 80 //拿三把 81 for(i=0;i<=N;i++) 82 if(ans[i]) 83 printf("%d %lld\n",i,ans[i]); 84 return 0; 85 }
本文为博主原创文章,转载请注明出处。