1. 程式人生 > >bzoj 3456: 城市規劃 NTT+多項式求逆

bzoj 3456: 城市規劃 NTT+多項式求逆

題意

求出n個點的簡單(無重邊無自環)無向連通圖數目。
你只需要輸出方案數mod 1004535809(479 * 2 ^ 21 + 1)即可.
n<=130000

分析

首先要知道遞推式

f[i]=2c2ij=1i1f[j]Cj1i12C2ij
如果不優化直接算的話顯然是不能夠接受的。考慮化簡式子。
將組合數展開,兩邊同除(i1)!f[i](i1)!=2C2i(i1)!j=1i1f[j](j1)!2C2ij(ij)! 2C2i(i1)!=j=1if[j](j1)!2C2ij(ij)!
容易發現這是一個卷積的形式。
A[i]=f[i]
(i1)!
,B[i]=2C2ii!,C[i]=2C2i(i1)!

那麼有C=AB=>A=CB1
上多項式求逆即可。

多項式求逆:
現要求AG=1(modxm)
已求出B滿足AB=1(modxm2)
因為AG=1(modxm2)
所以(GB)=0(modxm2)
兩邊平方得G2+B22GB=0(modxm)
G2=2GBB2(modxm)
兩邊同乘A得G=2BAB2

程式碼

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm> #include<cmath> using namespace std; typedef long long LL; const int N=300005; const int MOD=1004535809; int n,b[N],c[N],ny_b[N],jc[N],ny[N],rev[N],tmp[N]; int ksm(int x,int y) { int ans=1; while (y) { if (y&1) ans=(LL)ans*x%MOD; x=(LL)x*x
%MOD;y>>=1; } return ans; } void fft(int *a,int n,int f) { int lg=log(n)/log(2)+0.1; for (int i=0;i<n;i++) { rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1)); if (i<rev[i]) swap(a[i],a[rev[i]]); } for (int i=1;i<n;i*=2) { int wn=ksm(3,f==1?(MOD-1)/i/2:MOD-1-(MOD-1)/i/2); for (int j=0;j<n;j+=i*2) { int w=1; for (int k=0;k<i;k++) { int u=a[j+k],v=(LL)a[j+k+i]*w%MOD; a[j+k]=(u+v)%MOD;a[j+k+i]=(u-v+MOD)%MOD; w=(LL)w*wn%MOD; } } } if (f==-1) { int w=ksm(n,MOD-2); for (int i=0;i<n;i++) a[i]=(LL)a[i]*w%MOD; } } void get_ny(int *a,int *b,int n) { if (n==1) { b[0]=ksm(a[0],MOD-2); return; } get_ny(a,b,n/2); memcpy(tmp,a,sizeof(a[0])*n); memset(tmp+n,0,sizeof(tmp[0])*n); fft(tmp,n<<1,1);fft(b,n<<1,1); for (int i=0;i<n*2;i++) tmp[i]=(LL)b[i]*(2-(LL)tmp[i]*b[i]%MOD+MOD)%MOD; fft(tmp,n<<1,-1); for (int i=0;i<n;i++) b[i]=tmp[i]; memset(b+n,0,sizeof(b[0])*n); } int main() { scanf("%d",&n); ny[0]=jc[0]=1; for (int i=1;i<=n;i++) jc[i]=(LL)jc[i-1]*i%MOD,ny[i]=ksm(jc[i],MOD-2); for (int i=0;i<=n;i++) b[i]=(LL)ksm(2,(LL)i*(i-1)/2%(MOD-1))*ny[i]%MOD; for (int i=1;i<=n;i++) c[i]=(LL)ksm(2,(LL)i*(i-1)/2%(MOD-1))*ny[i-1]%MOD; int m; for (m=1;m<=n;m*=2); get_ny(b,ny_b,m); fft(c,m<<1,1);fft(ny_b,m<<1,1); for (int i=0;i<m*2;i++) c[i]=(LL)c[i]*ny_b[i]%MOD; fft(c,m<<1,-1); printf