1. 程式人生 > >【BZOJ3771】Triple 生成函數+FFT

【BZOJ3771】Triple 生成函數+FFT

ron 家裏 desc tchar pre 是個 走了 log fin

【BZOJ3771】Triple

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

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.

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