bzoj 3456: 城市規劃 NTT+多項式求逆
阿新 • • 發佈:2018-12-24
題意
求出n個點的簡單(無重邊無自環)無向連通圖數目。
你只需要輸出方案數mod 1004535809(479 * 2 ^ 21 + 1)即可.
n<=130000
分析
首先要知道遞推式
如果不優化直接算的話顯然是不能夠接受的。考慮化簡式子。
將組合數展開,兩邊同除
容易發現這是一個卷積的形式。
設
那麼有
上多項式求逆即可。
多項式求逆:
現要求
已求出
因為
所以
兩邊平方得
兩邊同乘A得
程式碼
#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