【CTSC2010】性能优化
循环卷积,快速傅里叶变换
题目大意:
给定长度为 $n$ 的数列 $A,B$ 和正整数 $C$。
求 $A\times B^C$ 在循环卷积下的答案。
系数对质数 $n+1$ 取模,保证 $n$ 只包含 $2,3,5,7$ 这些质因数。
题解:
$\omega_n^n=\omega_n^0$,DFT 本身就是循环卷积。
所以我们对原数列进行长度为 $n$ 的 DFT,然后直接点值快速幂,再 IDFT 回来就是循环卷积后的结果。
考虑 FFT 算法本身的实质,它相当于每次把原序列分成均等的两部分,然后进行分治,再进行合并。所以才需要把序列长度扩充为 $2^n$。
带入 $n$ 次单位根进行分治。
我们做长度为 $n$ 的 DFT,也需要每次分成均等的若干部分。
由于保证 $n$ 的质因子只有 $2,3,5,7$,所以每层最多分为 $7$ 份进行递归。
时间复杂度可以认为是 $O(n\log n)$。
Code:
#include<iostream>
#include<cstring>
using namespace std;
typedef long long LL;
const int N=524288;
int n,K,md,A[N],B[N],g,tmp[N];
inline int pow(int a,int b){
int ret=1;
for(;b;b>>=1,a=(LL)a*a%md)if(b&1)ret=(LL)ret*a%md;
return ret;
}
int find(){
for(int g=2;;++g){
if(n%2==0&&pow(g,n/2)==1)continue;
if(n%3==0&&pow(g,n/3)==1)continue;
if(n%5==0&&pow(g,n/5)==1)continue;
if(n%7==0&&pow(g,n/7)==1)continue;
return g;
}
}
void FFT(int*A,int n,const int g){
if(n==1)return;
const int bk=(n%2==0?2:(n%3==0?3:(n%5==0?5:7))),S=n/bk;
int it=0;
for(int K=0;K<bk;++K)
for(int i=K;i<n;i+=bk)
tmp[it++]=A[i];
memcpy(A,tmp,sizeof(int)*n);
const int g_=pow(g,bk);
for(int i=0;i<n;i+=S)
FFT(A+i,S,g_);
memset(tmp,0,sizeof(int)*n);
for(int i=0;i<n;++i){
const int gi=pow(g,i);
for(int k=0,G=1;k<bk;++k,G=(LL)G*gi%md)
tmp[i]=(tmp[i]+(LL)G*A[i%S+S*k])%md;
}
memcpy(A,tmp,sizeof(int)*n);
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>K,md=n+1;
g=find();
for(int i=0;i<n;++i)cin>>A[i];
for(int i=0;i<n;++i)cin>>B[i];
FFT(A,n,g),FFT(B,n,g);
for(int i=0;i<n;++i)A[i]=(LL)A[i]*pow(B[i],K)%md;
FFT(A,n,pow(g,md-2));
const int iv=pow(n,md-2);
for(int i=0;i<n;++i)A[i]=(LL)A[i]*iv%md;
for(int i=0;i<n;++i)
cout<<A[i]<<'\n';
return 0;
}