分治FFT


分治FFT


引入問題:摘自洛谷P4721 【模板】分治 FFT,描述如下:

給出多項式\(g[0..n]\),求多項式\(f\),滿足:

\[f(i)=\sum_{j=1}^if(i-j)g(j) \]

邊界\(f(0)=1\)


注意到這是個卷積的形式,不難想到\(FTT\),但是這里卷積內有一個\(f\)\(f\)是未知的,就不能用常規的多項式乘法了。

我們引入一個叫做分治\(FFT\)的算法,其基本思想很簡單,就是和\(cdq\)分治一樣,我們先分治左邊,然后考慮左邊對右邊的影響,再分治右邊。

具體的,假設我們現在已經做完了\([l,mid]\),考慮對右邊的影響。

我們可以把式子列出來,就是:

\[f(i)=\sum_{j=l}^{mid}f(j)g(i-j) \]

那么我們可以把\(f\)\([l,mid]\)項拿出來,其他項置\(0\),在把這個和\(g\)\([0,r-l]\)卷起來就可以得到影響,然后加上去就好了。

#include<bits/stdc++.h>
using namespace std;
 
void read(int &x) {
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
 
void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}

#define lf double
#define ll long long 

const int maxn = 2e5+10;
const int inf = 1e9;
const lf eps = 1e-8;
const int mod = 998244353;

int f[maxn],g[maxn],mxn,w[maxn],n,N,rw[maxn],t[maxn],bit,pos[maxn],s[maxn];

int qpow(int a,int x) {
	int res=1;
	for(;x;x>>=1,a=1ll*a*a%mod) if(x&1) res=1ll*res*a%mod;
	return res;
}

void prepare() {
	w[0]=1;w[1]=qpow(3,(mod-1)/mxn);
	for(int i=2;i<=mxn;i++) w[i]=1ll*w[i-1]*w[1]%mod;
	rw[0]=1,rw[1]=qpow(qpow(3,mod-2),(mod-1)/mxn);
	for(int i=2;i<=mxn;i++) rw[i]=1ll*rw[i-1]*rw[1]%mod;
}

void ntt(int *r,int op) {
	for(int i=1;i<N;i++) if(pos[i]>i) swap(r[i],r[pos[i]]);
	for(int i=1,d=mxn>>1;i<N;i<<=1,d>>=1) 
		for(int j=0;j<N;j+=i<<1)
			for(int k=0;k<i;k++) {
				int x=r[j+k],y=1ll*r[i+j+k]*(op==1?w:rw)[k*d]%mod;
				r[j+k]=(x+y)%mod,r[i+j+k]=(x-y+mod)%mod;
			}
	if(op==-1) {
		int inv=qpow(N,mod-2);
		for(int i=0;i<N;i++) r[i]=1ll*r[i]*inv%mod;
	}
}

void solve(int l,int r) {
	if(l==r) return ;
	int mid=(l+r)>>1;
	solve(l,mid);
	for(int i=l;i<=mid;i++) t[i-l]=f[i],s[i-l]=g[i-l];
	for(int i=mid+1;i<=r;i++) t[i-l]=0,s[i-l]=g[i-l];
	for(bit=0,N=1;N<=r-l;N<<=1,bit++);
	for(int i=r-l+1;i<N;i++) t[i]=s[i]=0;
	for(int i=1;i<N;i++) pos[i]=pos[i>>1]>>1|((i&1)<<(bit-1));
	ntt(s,1),ntt(t,1);
	for(int i=0;i<N;i++) s[i]=1ll*s[i]*t[i]%mod;
	ntt(s,-1);
	for(int i=mid+1;i<=r;i++) f[i]=(f[i]+s[i-l])%mod;
	solve(mid+1,r);
}

int main() {
	read(n);
	for(int i=1;i<n;i++) read(g[i]);f[0]=1;
	for(mxn=1;mxn<n;mxn<<=1);
	prepare();
	solve(0,mxn-1);
	for(int i=0;i<n;i++) printf("%d ",f[i]);puts("");
	return 0;
}

當然這個題也可以用生成函數做,設:

\[F(x)=\sum_{i=0}^{+\infty}f(i)x^i,G(x)=\sum_{i=0}^{+\infty}g(i)x^i \]

乘起來:

\[F(x)G(x)=\sum_{i=0}^{+\infty}x^i\sum_{j=0}^if(j)g(i-j) \]

由於\(g(0)=0\),可以得到:

\[F(x)G(x)=\sum_{i=1}^{+\infty}f(i)x^i=F(x)-1 \]

所以:

\[\begin{align} F(x)G(x)&\equiv F(x)-1 \pmod{x^n}\\ F(x)&\equiv \frac{1}{1-G(x)} \pmod{x^n}\\ \end{align} \]

那么多項式求逆就做完了。

代碼咕咕咕


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM