Jzoj3303 城市规划
刚刚解决完电力网络的问题, 阿狸又被领导的任务给难住了.
刚才说过, 阿狸的国家有n 个城市, 现在国家需要在某些城市对之间建立一些贸易路线, 使得整个国家的任意两个城市都直接或间接的连通.
为了省钱, 每两个城市之间最多只能有一条直接的贸易路径. 对于两个建立路线的方案, 如果存在一个城市对, 在两个方案中是否建立路线不一样, 那么这两个方案就是不同的, 否则就是相同的. 现在你需要求出一共有多少不同的方案.
好了, 这就是困扰阿狸的问题. 换句话说, 你需要求出n 个点的简单(无重边无自环)无向连通图数目.
由于这个数字可能非常大, 你只需要输出方案数mod 1004535809(479 * 2 ^21 + 1)即可.
NTT第一道例题
我们令f[k]表示n个点的图的无向图个数,显然f[k]=2^C(k,2)
令g[k]表示k个点的无向连通图个数,用dp计算就得到g[k]=f[k]-ΣC(k-1,i-1)*g[i]*f[k-i] (1<=i<k)
g[k]=f[k]-(k-1)!Σg[i]/(i-1)!*f[k-i]/(k-i)!
g[k]=f[k]-(k-1)!Σg'[i]*f'[k-i]
发现不能直接做,我们考虑用分治,计算g[l..m]对g[m+1,r]的贡献,这样一次就是O(n lg n)的
总复杂度O(n lg^2 n)
#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 280010
#define Rg register
#define LL long long
#define M 1004535809
using namespace std;
int n,nL,m,a[N],b[N],z[N];
LL f[N],g[N],js[N],inv[N],W[N],iW[N];
inline int p2(int x,int t=1){
for(;t<x;t<<=1); return t<<1;
}
inline int lg(int x,int t=0){
for(;x>1;x>>=1) ++t; return t;
}
inline int pow(LL x,LL k,LL& s){
for(s=1;k;x=x*x%M,k>>=1) k&1?s=s*x%M:0;
}
void NTT(int* a,int* b,int n,int g){
for(int i=0,j,k,t;i<n;++i){
for(j=0,k=i,t=n-1;t;t>>=1,k>>=1) j=(j<<1)|(k&1);
b[j]=a[i];
}
for(int m=2;m<=n;m<<=1){
Rg LL w=g?W[m]:iW[m],u,v,z;
for(Rg int i,k=m>>1,j=0;j<n;j+=m)
for(z=1,i=0;i<k;++i,z=z*w%M){
u=b[i+j]; v=z*b[i+j+k]%M;
b[i+j]=(u+v)%M; b[i+j+k]=(u-v+M)%M;
}
}
memcpy(a,b,n<<2);
}
void MUL(int l,int r,int m){
LL iv; int n=p2(m-l+1);
memset(a,0,n<<2);
memset(b,0,n<<2);
for(int i=1;i<=r-l+1;++i)
a[i]=g[i+l-1]*inv[i+l-2]%M;
for(int i=1;i<=m-l;++i)
b[i]=f[i]*inv[i]%M;
NTT(a,z,n,1); NTT(b,z,n,1);
for(int i=0;i<n;++i) a[i]=(LL)a[i]*b[i]%M;
NTT(a,z,n,0); pow(n,M-2,iv);
for(int i=0;i<n;++i) a[i]=(LL)a[i]*iv%M;
}
void cdq(int l,int r){
if(l==r) g[l]=(f[l]-g[l]*js[l-1]%M+M)%M;
else{
int m=l+r>>1;
cdq(l,m);
MUL(l,m,r);
for(int i=m+1;i<=r;++i) g[i]=(g[i]+a[i-l+1])%M;
cdq(m+1,r);
}
}
int main(){
scanf("%d",&n);
for(int i=*js=*inv=1;i<=n;++i) js[i]=js[i-1]*i%M;
pow(js[n],M-2,inv[n]); for(int i=n;i;--i) inv[i-1]=inv[i]*i%M;
for(int i=1;i<=n;++i) pow(2,i*(i-1ll)>>1,f[i]);
for(int j=1;j<=N;j<<=1){
pow(3,(M-1)/j,W[j]); pow(W[j],M-2,iW[j]);
}
cdq(1,p2(n)>>1);
printf("%lld\n",g[n]);
}