University Training Contest 2 快速傅里叶变换

He is FlyingTime Limit: 10000/5000 MS (Java/Others)Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 91Accepted Submission(s): 25

Problem Description

JRY wants to drag racing along a long road. There aresections on the road, the-th section has a non-negative integer length. JRY will choose some continuous sections to race (at an unbelievable speed), so there are totallydifferent ways for him to ride. If JRY rides across from the-th section to the-th section, he would gainpleasure. Now JRY wants to know, if he tries all the ways whose length is, what’s the total pleasure he can get. Please be aware that in the problem, the length of one section could be zero, which means that the length is so trivial that we can regard it as.

Input

The first line of the input is a single integer, indicating the number of testcases.For each testcase, the first line contains one integer. The second line containsnon-negative integers, which mean the length of every section. If we denote the total length of all the sections as, we can guarantee that.

Output

For each testcase, printlines. The single number in the-th line indicates the total pleasure JRY can get if he races all the ways of length.

Sample Input

231 2 340 1 2 3

Sample Output

01130231316027

Source

题目:

给一个数组有N个数字,每个数字都大于等于0,,所有数字相加不大于50000, 对于区间[l,r]如果区间点数和为S‘,那么S’的值加上r-l+1。

令sum为所有数字的总和,对(N+1)*N/2个区间进行处理计算S’和增加对应的值,最后输出0到sum的每个数的值。

分析:

普通的做法要N*N的,枚举所有的区间。但是数组太大会超时的。

由于sum <= 50000考虑构造母函数进行求解 用Si表示前i个数字的前缀和。构造如下多项式,对于计算的结果A*X^Si表示区间和为Si能够得到值为A

会fft的应该就会懂这个多项式的意义(所以不多说)。

用X^Si * X*-Sj 得到的就是区间[j+1,i]的区间和,带上系数i,则表示区间长度为i。但是我们实际要得到的是i-j的值,所以第二个乘法的目的就是减去这个j。

最后0的情况特殊处理即可。FFT用double可能精度不够用long double好。标程用的是整数进行计算的。看不懂。我的模板用long double 就行了。在hdu上要用g++提交,不然也是会wa的。标程也是要用g++提交,不然就T啦。

还要注意:

负数次幂要变成整数次幂,把sum算出来,每个负数次幂加上一个sum即可。

在每个乘法中第二个括号内,要添加一个X^0的数,表示可以选着区间[1,x]否则可能算不出[1,x]的结果(x表示>=1,<=N)的数字

直接贴代码:

我的代码:

#include<iostream>#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>using namespace std;#define ll long long#define ld long doublestruct Complex{ld real;ld imag;Complex (){}Complex(ld the){real = cos(the);imag = sin(the);}Complex(ld a,ld b){real = a;imag = b;}};Complex operator + (Complex a,Complex b){return Complex(a.real+b.real,a.imag+b.imag);}Complex operator – (Complex a,Complex b){return Complex(a.real-b.real,a.imag-b.imag);}Complex operator * (Complex a,Complex b){return Complex(a.real*b.real-a.imag*b.imag,a.imag*b.real+a.real*b.imag);}#define maxn 300000ld pi2 = 2*acos(-1.0);void fft(Complex* A,int len, int ref){//A[rev[k]] = ak 系数向量转置int bitn = log2(len);int i,j,k;for(i = 0;i < len; i++){k = 0;for( j = 0;j < bitn; j++){k = (k<<1);if(i&(1<<j))k |= 1;}if(k > i)swap(A[i],A[k]);}//fft计算得到点值Complex wm,w,t,u;for(int m = 2,f=1; m <= len; m<<=1,f<<=1){wm = Complex(ref*pi2/m);for( k = 0;k < len; k += m){w = Complex(1.0,0);for( j = k; j < k+f; j++){t = w*A[j+f];u = A[j];A[j] = u+t;A[j+f] = u-t;w = w*wm;}}}if(ref == -1){for( i = 0;i < len; i++){A[i].real = A[i].real/len;}}}Complex a1[maxn];Complex a2[maxn];int num[maxn];int sum[maxn];ll ans[maxn];ll snum[maxn];int main(){int t,n;//freopen("h.in","r",stdin);//freopen("hh.out","w",stdout);scanf("%d",&t);snum[0] = 0;for(ll i = 1; i <= 100000; i++)snum[i] = snum[i-1] + i*(i+1)/2;while(t–){scanf("%d",&n);for(int i = 0;i < n; i++)scanf("%d",&num[i]);sum[0] = num[0];for(int i = 1;i < n; i++)sum[i] = sum[i-1] + num[i];ll p = 0 ,res = 0,lnum = 0;for(int i = 0;i < n; i++){if(num[i] == 0)p++;else {res += snum[p];p = 0;}}res += snum[p];printf("%I64d\n",res);int total = sum[n-1];int total2 = total*2;int len = 1;while(len <= total2) len*=2;memset(a1,0,sizeof(a1));memset(a2,0,sizeof(a2));for(int i = 0; i < n; i++){a1[sum[i]].real += i+1;if(i != n-1)a2[total-sum[i]].real += 1;}a2[total].real += 1;fft(a1,len,1);fft(a2,len,1);for(int i = 0;i <= len; i++)a1[i] = a1[i]*a2[i];fft(a1,len,-1);for(int i = 0;i <= len; i++)ans[i] = (ll)(a1[i].real+0.5);memset(a1,0,sizeof(a1));memset(a2,0,sizeof(a2));for(int i = 0;i < n; i++){a1[sum[i]].real += 1;if(i != n-1)a2[total-sum[i]].real += i+1;}fft(a1,len,1);fft(a2,len,1);for(int i = 0;i <= len; i++)a1[i] = a1[i]*a2[i];fft(a1,len,-1);for(int i = 0;i <= len; i++)ans[i] -= (ll)(a1[i].real+0.5);for(int i = total+1;i <= total2; i++)printf("%I64d\n",ans[i]);}return 0;}

标程的代码:确实厉害。留着以防不时之需却只能这样。只有对爱的人,我们才会斤斤计较,锱铢必较。

University Training Contest 2 快速傅里叶变换

相关文章:

你感兴趣的文章:

标签云: