1. 程式人生 > >HDU5307 He is Flying 【FFT】

HDU5307 He is Flying 【FFT】

題目描述:

n段連續編號為1~n的道路,每段長度為一個非負整數,道路總長<=50000,n<=100000
從編號為 j j 的路走到編號為 i i 的路所得到的快樂值為 j

i + 1 j-i+1
對於每個 S S ,( 0
S s i 0\le S\le\sum si
),求出走所有長度為S的路(可以是幾段接起來的)得到的快樂值
簡述:數列 {
s n } \{s_n\}
,總和為 S u m Sum ,求 k [ 0 , S u m ] k\in[0,Sum] ,所有區間和為k的區間長度和

題目分析:

把加法運算轉化為冪的次數,再利用係數得到答案
s i s j &lt; = &gt; x s i     x s j si-sj&lt;=&gt;x^{si}~*~x^{-sj}
那麼所有的區間就可以表示成多項式相乘的形式:
( i = 1 n x s i ) ( j = 0 n 1 x s j ) \left(\sum_{i=1}^nx^{si}\right)*\left(\sum_{j=0}^{n-1}x^{-sj}\right)
乘出來的 x k x^k 項的係數就是長度為k的區間的個數,當i<=j時次數為非正數,對答案無影響
(所以要特殊處理長度為0的情況,預處理,O(n)掃一遍即可)
那麼再考慮把區間長度加進去,就是:
( i = 1 n i x s i ) ( j = 0 n 1 x s j ) ( i = 1 n x s i ) ( j = 0 n 1 j x s j ) \left(\sum_{i=1}^nix^{si}\right)*\left(\sum_{j=0}^{n-1}x^{-sj}\right)-\left(\sum_{i=1}^nx^{si}\right)*\left(\sum_{j=0}^{n-1}jx^{-sj}\right)
做兩次FFT即可,第二個多項式由於次數為負,所以右移sum次 ( x s u m s j ) (x^{sum-sj})

  • double最多有15位有效數字,而此題答案可能大於1015,所以要用 long double,或者寫NTT,不過模數要很大,還要寫大數模乘法。。。
#include<cstdio>
#include<cmath>
#include<algorithm>
#define maxn 400005
using namespace std;
const long double Pi = acos(-1);
struct complex
{
	long double r,i;
	complex(long double _r=0,long double _i=0):r(_r),i(_i){}
	complex operator + (const complex &t)const{return complex(r+t.r,i+t.i);}
	complex operator - (const complex &t)const{return complex(r-t.r,i-t.i);}
	complex operator * (const complex &t)const{return complex(r*t.r-i*t.i,r*t.i+i*t.r);}
}x1[maxn],x2[maxn],w,wn;
void change(complex *x,int len)
{
	for(int i=1,j=len/2,k;i<len-1;i++)
	{
		if(i<j) swap(x[i],x[j]);
		for(k=len/2;j>=k;j-=k,k>>=1);
		j+=k;
	}
}
void fft(complex *x,int len,int flg)
{
	change(x,len);
	for(int i=2;i<=len;i<<=1)
	{
		wn=complex(cos(2*Pi*flg/i),sin(2*Pi*flg/i));
		for(int j=0;j<len;j+=i)
		{
			w=complex(1,0);
			for(int k=j;k<j+i/2;k++)
			{
				complex u=x[k],v=w*x[k+i/2];
				x[k]=u+v,x[k+i/2]=u-v;
				w=w*wn;
			}
		}
	}
	if(flg==-1) for(int i=0;i<len;i++) x[i].r/=len;
}
int T,n,s[maxn];
long long ans[maxn],sum[maxn];
int main()
{
	for(int i=1;i<=100000;i++) sum[i]=sum[i-1]+i*(i+1)/2;//0的預處理
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		for(int i=1;i<=n;i++) scanf("%d",&s[i]),s[i]+=s[i-1];
		int len=1;while(len<=2*s[n]) len<<=1;
		for(int i=0;i<len;i++) x1[i]=x2[i]=complex();
		for(int i=1;i<=n;i++) x1[s[i]].r+=i;
		for(int i=0;i<n;i++) x2[s[n]-s[i]].r++;
		fft(x1,len,1),fft(x2,len,1);
		for(int i=0;i<len;i++) x2[i]=x1[i]*x2[i];
		fft(x2,len,-1);
		for(int i=0;i<len;i++) ans[i]=(long long)(x2[i].r+0.5);
		for(int i=0;i<len;i++) x1[i]=x2[i]=complex();
		for(int i=1;i<=n;i++) x1[s[i]].r++;
		for(int i=0;i<n;i++) x2[s[n]-s[i]].r+=i;
		fft(x1,len,1),fft(x2,len,1);
		for(int i=0;i<len;i++) x2[i]=x1[i]*x2[i];
		fft(x2,len,-1);
		for(int i=0;i<len;i++) ans[i]-=(long long)(x2[i].r+0.5);
		ans[s[n]]=0,s[n+1]=-1;
		for(int i=1,tmp=0;i<=n+1;i++)
			if(s[i]==s[i-1]) tmp++;
			else ans[s[n]]+=sum[tmp],tmp=0;
		for(int i=0;i<=s[n];i++) printf("%lld\n",ans[i+s[n]]);
	}
}