HDU5307 He is Flying 【FFT】

版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/C20181220_xiang_m_y/article/details/85145189

题目描述:

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]]);
	}
}

猜你喜欢

转载自blog.csdn.net/C20181220_xiang_m_y/article/details/85145189
he
FFT