[ZJOI2014][bzoj3527]力 [FFT] 题面 思路 Code:

传送门

思路

把要求的公式列出来:

$E_i=frac{F_i}{q_i}=sum_{j=1}ifrac{q_j}{left(i-j ight)2}-sum_{j=i+1}nfrac{q_j}{left(i-j ight)2}$

令$x_i=frac1{i^2}$,那么

$E_i=sum_{j=1}iq_jx_{i-j}-sum_{j=i+1}nq_jx_{j-i}$

那我们再令$p_i=q_{n-i+1}$,那么

$E_i=sum_{j=1}iq_jx_{i-j}-sum_{j=i+1}np_{n-j}x_{j-i}$

此时我们发现式子的左侧和右侧都是一个卷积的形式

那么,我们就可以用FFT来维护这个过程了

将数列$q_i$,$p_i$,$x_i$作为多项式$A$,$B$,$C$的系数

将他们用fft乘起来,得到的$Aast C$,$Bast C$的系数做差,就是$E_i$的值

Code:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
struct complex{
    double x,y;
    complex(double xx=0,double yy=0){x=xx;y=yy;}
    complex operator +(const complex &b){return complex(b.x+x,b.y+y);}
    complex operator -(const complex &b){return complex(-b.x+x,-b.y+y);}
    complex operator *(const complex &b){return complex(x*b.x-y*b.y,x*b.y+y*b.x);}
}A[410010],B[410010],C[410010];
const double pi=acos(-1.0);
int n,limit=1,cnt=0,r[410010];
void fft(complex *a,double type){
    int i,mid,j,k,R;complex w,wn,x,y;
    for(i=0;i<limit;i++) if(i<r[i]) std::swap(a[i],a[r[i]]);
    for(mid=1;mid<limit;mid<<=1){
        wn=complex(cos(pi/mid),type*sin(pi/mid));
        for(R=mid<<1,j=0;j<limit;j+=R){
            w=complex(1,0);
            for(k=0;k<mid;k++,w=w*wn){
                x=a[j+k];y=w*a[j+k+mid];
                a[j+k]=x+y;
                a[j+k+mid]=x-y;
            }
        }
    }
}
int main(){
    scanf("%d",&n);int i;
    for(i=1;i<=n;i++) scanf("%lf",&A[i].x),B[n+1-i].x=A[i].x;
    for(i=1;i<=n;i++) C[i].x=(1.0/double(i))/double(i);
    
    while(limit<=(n<<1)) limit<<=1,cnt++;
    for(i=0;i<limit;i++) r[i]=((r[i>>1]>>1)|((i&1)<<(cnt-1)));
    
    fft(A,1);fft(B,1);fft(C,1);
    for(i=0;i<=limit;i++) A[i]=A[i]*C[i],B[i]=B[i]*C[i];
    fft(A,-1);fft(B,-1);
    for(i=0;i<=limit;i++) A[i].x/=limit,B[i].x/=limit;
    
    for(i=1;i<=n;i++) printf("%.4lf
",-B[n+1-i].x+A[i].x);
}