求 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,...,fn−1已知。
简单的例子:
f n = ∑ i = 0 n f i ∗ g n − i f_n=\sum_{i=0}^nf_i*g_{n-i} fn=∑i=0nfi∗gn−i
普通解法直接 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} fx∗gy−x
又 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=lmidfx∗gy−x
所以用 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=0iAi∗Bj
所以, f [ l , m i d ] f_{[l,mid]} f[l,mid]给 f y f_y fy贡献了: C i − l C_{i-l} Ci−l
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);
}
求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=gn−∑i=1n−1C(n−1,i−1)∗fi∗gn−i
优化:分治NTT
令 f n = g n − S U M f_n=g_n-SUM fn=gn−SUM
则 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=(i−1)!∑j=1i(i−1)!fi∗(n−i)!gn−i
发现了卷积形式了吧?
代码
#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;
}