分治NTT学习小记

简述问题

f n f_n fn,其中 f n f_n fn的值需要 f 0 , f 1 , . . . , f n − 1 f_0,f_1,...,f_{n-1} f0,f1,...,fn1已知。
简单的例子:
f n = ∑ i = 0 n f i ∗ g n − i f_n=\sum_{i=0}^nf_i*g_{n-i} fn=i=0nfigni

解法

普通解法直接 O ( n 2 ) O(n^2) O(n2)暴力搞。
快些?分治NTT。
思想:CDQ分治。考虑分值区间 [ l , r ] [l,r] [l,r] f [ l , m i d ] f_{[l,mid]} f[l,mid] f [ m i d + 1 , r ] f_{[mid+1,r]} f[mid+1,r]的贡献。
考虑 f x , x ∈ [ l , m i d ] f_x,x∈[l,mid] fx,x[l,mid] f y , y ∈ [ m i d + 1 , r ] f_y,y∈[mid+1,r] fy,y[mid+1,r]的影响。
显然, f x f_x fx只对 f y f_y fy贡献了 f x ∗ g y − x f_x*g_{y-x} fxgyx
f y = S U M ( 前 面 的 ) + ∑ x = l m i d f x ∗ g y − x f_y=SUM(前面的)+\sum_{x=l}^{mid}f_x*g_{y-x} fy=SUM()+x=lmidfxgyx
所以用 O ( n   l o g   n ) O(n\ log\ n) O(n log n)的算法将某两个多项式 A , B A,B A,B乘起来是很有必要的。
A i = f i + l , B i = g i A_i=f_{i+l},B_i=g_i Ai=fi+l,Bi=gi C i = ∑ j = 0 i A i ∗ B j C_i=\sum_{j=0}^{i}A_i*B_j Ci=j=0iAiBj
所以, f [ l , m i d ] f_{[l,mid]} f[l,mid] f y f_y fy贡献了: C i − l C_{i-l} Cil

伪代码

void solve(int le,int ri){
    if(le==ri){
        ...
        return;
    }
    int mid=(l+r)>>1;
    solve(le,mid);
    int i;
    ...//get L
    memset(f1/f2,0);
    fo(i,0,mid-l)f1[i]=f[i+l];
    f2[0]=0;
    fo(i,1,r-l)f2[i]=g[i];
    NTT(f1,1);NTT(f2,1);
    //f1*f2
    NTT(f1);
    solve(mid+1,ri);
}

例题

JZOJ 3303 城市规划

求n个点的无向连通图的个数,无自环,无重边。
题解
g n g_n gn为n个点的无向图的个数。(无自环,无重边)
显然, f n = g n − ∑ i = 1 n − 1 C ( n − 1 , i − 1 ) ∗ f i ∗ g n − i f_n=g_n-\sum_{i=1}^{n-1}C(n-1,i-1)*f_i*g_{n-i} fn=gni=1n1C(n1,i1)figni
优化:分治NTT
f n = g n − S U M f_n=g_n-SUM fn=gnSUM
S U M = ( i − 1 ) ! ∑ j = 1 i f i ( i − 1 ) ! ∗ g n − i ( n − i ) ! SUM=(i-1)!\sum_{j=1}^{i}\frac{f_i}{(i-1)!}*\frac{g_{n-i}}{(n-i)!} SUM=(i1)!j=1i(i1)!fi(ni)!gni
发现了卷积形式了吧?
代码

#include
#include
#include
#include
#include
#define N 400010
#define mo 1004535809
#define LL long long
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
int i,j,k,l,n,m,ans;
int L;
int jc[N],ny[N];
int g[N],f[N],f1[N],f2[N];
int w[N],_w[N],inv[N],Rev[N];
int ksm(int x,LL y){
	int rs=1;
	for(;y;y>>=1,x=(1ll*x*x)%mo)if(y&1)rs=(1ll*rs*x)%mo;
	return rs;
}
void pre(){
	int i;
	jc[0]=jc[1]=ny[0]=ny[1]=1;
	fo(i,2,N-10)jc[i]=(1ll*jc[i-1]*i)%mo;
	ny[N-10]=ksm(jc[N-10],mo-2);
	fd(i,N-11,2)ny[i]=(1ll*ny[i+1]*(i+1))%mo; 
}
void Swap(int &x,int &y){
	x=x^y;y=x^y;x=x^y;
}
void NTT(int *y,int opz,int LEN){
	int i,len,hf;
	fo(i,0,L-1)if(i<Rev[i])Swap(y[i],y[Rev[i]]);
	for(len=2;len<=L;len<<=1){
		hf=len>>1;
		int wn=opz==1?w[len]:_w[len],w;
		for(j=0;j<L;j+=len){
			w=1;
			fo(i,j,j+hf-1){
				int u=y[i],t=(1ll*w*y[i+hf])%mo;
				y[i]=(u+t)%mo;
				y[i+hf]=(u-t+mo)%mo;
				w=(1ll*w*wn)%mo;
			}
		}
	}
	if(opz==-1){
		fo(i,0,L-1)y[i]=(1ll*y[i]*inv[L])%mo;
	}
}
void cdq(int l,int r){
	if(l==r){
		f[l]=(g[l]-(1ll*f[l]*jc[l-1])%mo+mo)%mo;
		return;
	}
	int i,wz=(l+r)>>1,cf=0;
	cdq(l,wz);
	for(L=1;L<2*(r-l+1);L<<=1)cf++;
	Rev[0]=0;
	fo(i,1,L-1)Rev[i]=Rev[i>>1]>>1|((i&1)<<(cf-1));	
	fo(i,0,wz-l)f1[i]=1ll*f[i+l]*ny[i-1+l]%mo;
	fo(i,wz-l+1,L-1)f1[i]=0;
	f2[0]=0;
	fo(i,1,r-l)f2[i]=1ll*g[i]*ny[i]%mo;
	fo(i,r-l+1,L-1)f2[i]=0;
	NTT(f1,1,L);
	NTT(f2,1,L);
	fo(i,0,L-1)f1[i]=1ll*f1[i]*f2[i]%mo;
	NTT(f1,-1,L);
	fo(i,wz+1,r){
		f[i]=(f[i]+f1[i-l])%mo;
	}
	cdq(wz+1,r);
}
int main(){
	scanf("%d",&n);
	pre();
	g[0]=0;f[0]=0;
	fo(i,1,n*2)g[i]=ksm(2,1ll*i*(i-1)>>1);
	w[0]=1;
	for(L=1;L<(n*4);L<<=1){
		w[L]=ksm(3,(mo-1)/L);
		_w[L]=ksm(w[L],mo-2);
		inv[L]=ksm(L,mo-2);
	}
	cdq(1,n);
	printf("%d",f[n]);
	return 0;
}

你可能感兴趣的:(FFT)