【BZOJ3771】Triple 生成函數+FFT
阿新 • • 發佈:2017-08-22
ron 家裏 desc tchar pre 是個 走了 log fin
於是他又一次答:“是啊是啊!真的是!”
水神看著他,哈哈大笑道:
“你看看你現在的樣子,真是醜陋!”
之後就消失了。
樵夫覺得很坑爹,他今天不僅沒有砍到柴,還丟了一把斧頭給那個水神。
於是他準備回家換一把斧頭。
回家之後他才發現真正坑爹的事情才剛開始。
水神拿著的的確是他的斧頭。
但是不一定是他拿出去的那把,還有可能是水神不知道怎麽偷偷從他家裏拿走的。
換句話說,水神可能拿走了他的一把,兩把或者三把斧頭。
樵夫覺得今天真是倒黴透了,但不管怎麽樣日子還得過。
他想統計他的損失。
樵夫的每一把斧頭都有一個價值,不同斧頭的價值不同。總損失就是丟掉的斧頭價值和。
他想對於每個可能的總損失,計算有幾種可能的方案。
註意:如果水神拿走了兩把斧頭a和b,(a,b)和(b,a)視為一種方案。拿走三把斧頭時,(a,b,c),(b,c,a),(c,a,b),(c,b,a),(b,a,c),(a,c,b)視為一種方案。
4
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.
【BZOJ3771】Triple
Description
我們講一個悲傷的故事。 從前有一個貧窮的樵夫在河邊砍柴。 這時候河裏出現了一個水神,奪過了他的斧頭,說: “這把斧頭,是不是你的?” 樵夫一看:“是啊是啊!” 水神把斧頭扔在一邊,又拿起一個東西問: “這把斧頭,是不是你的?” 樵夫看不清楚,但又怕真的是自己的斧頭,只好又答:“是啊是啊!” 水神又把手上的東西扔在一邊,拿起第三個東西問: “這把斧頭,是不是你的?” 樵夫還是看不清楚,但是他覺得再這樣下去他就沒法砍柴了。Input
第一行是整數N,表示有N把斧頭。 接下來n行升序輸入N個數字Ai,表示每把斧頭的價值。Output
若幹行,按升序對於所有可能的總損失輸出一行x y,x為損失值,y為方案數。Sample Input
44
5
6
7
Sample Output
4 15 1
6 1
7 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
題解:當年以為這就是個桶,後來得知這玩意叫生成函數。
設所有斧頭的生成函數為x,那麽我們將x自乘1,2,3次,得到x,y,z,那麽考慮每種情況被計算的次數。
x——a:1次
y——aa:1次,ab:2次
z——aaa:1次,aab:3次,abc:6次
那就把aa,aaa也求出來,用aa*b-aaa得到aab,就全統計出來了。
#include <cstdio> #include <cstring> #include <iostream> #include <cmath> #define pi acos(-1.0) using namespace std; typedef long long ll; struct cp { double x,y; cp (double a,double b){x=a,y=b;} cp (){} cp operator + (cp a){return cp(x+a.x,y+a.y);} cp operator - (cp a){return cp(x-a.x,y-a.y);} cp operator * (cp a){return cp(x*a.x-y*a.y,x*a.y+y*a.x);} cp operator * (double a){return cp(x*a,y*a);} }n1[1<<19],n2[1<<19],n3[1<<19]; int n,m,top,len; int ans[1<<19]; ll s[1<<19]; void FFT(cp *a,int f) { int i,j,k,h; cp t; for(i=k=0;i<len;i++) { if(i>k) swap(a[i],a[k]); for(j=(len>>1);(k^=j)<j;j>>=1); } for(h=2;h<=len;h<<=1) { cp wn(cos(f*2*pi/h),sin(f*2*pi/h)); for(j=0;j<len;j+=h) { cp w(1,0); for(k=j;k<j+h/2;k++) t=w*a[k+h/2],a[k+h/2]=a[k]-t,a[k]=a[k]+t,w=w*wn; } } } int rd() { int ret=0,f=1; char gc=getchar(); while(gc<‘0‘||gc>‘9‘) {if(gc==‘-‘)f=-f; gc=getchar();} while(gc>=‘0‘&&gc<=‘9‘) ret=ret*10+gc-‘0‘,gc=getchar(); return ret*f; } int main() { n=rd(); int i,a; for(i=1;i<=n;i++) a=rd(),n1[a].x+=1,s[a]++,n2[a<<1].x+=1,n3[a*3].x+=1,m=max(m,a); for(len=1;len<=m*3;len<<=1); FFT(n1,1),FFT(n2,1),FFT(n3,1); for(i=0;i<len;i++) { cp x=n1[i],y=n2[i],z=n3[i]; n2[i]=((x*x)-y)*(1.0/2),n3[i]=((x*x*x)-(x*y*3.0)+(z*2.0))*(1.0/6); } FFT(n2,-1),FFT(n3,-1); for(i=0;i<len;i++) s[i]+=(ll)(n2[i].x/len+0.1)+(ll)(n3[i].x/len+0.1); for(i=0;i<len;i++) if(s[i]) ans[++top]=i; for(i=1;i<=top;i++) printf("%d %lld\n",ans[i],s[ans[i]]); return 0; }
【BZOJ3771】Triple 生成函數+FFT