链接:BZOJ3456
(权限题)
题解
首先,点有标号!
设 表示 个点组成的有标号连通无向图个数, 表示 个点组成的有标号无向图个数(不要求连通)。那这个 谁都会啦
可是有啥用呢
一个图是由若干强联通分量组成,令 为 节点所在强联通分量的大小,可以得到 的又一表达式
令
时间复杂度
代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const long long P=1004535809ll;
long long F[262145],invF[262145],G[262145],H[262145],fact[130001];
int rev[262145],n;
long long qpow(long long a,int x){
long long s=1ll;
while(x){if(x&1)(s*=a)%=P;(a*=a)%=P;x>>=1;}
return s;
}
void ntt(long long *f,int len,int check){
const static long long G=3ll,invG=334845270ll;
for(int i=0;i<len;++i)if(i<rev[i])swap(f[i],f[rev[i]]);
for(int i=1;i<len;i<<=1){
long long wn=qpow(check==1?G:invG,(P-1)/(i<<1));
for(int j=0;j<len;j+=(i<<1)){
long long w=1ll;
for(int k=0;k<i;++k){long long x=f[j+k],y=w*f[i+j+k]%P;f[j+k]=(x+y)%P,f[i+j+k]=(x-y+P)%P;(w*=wn)%=P;}
}
}
if(check==-1){long long invL=qpow(len,P-2);for(int i=0;i<len;++i)(f[i]*=invL)%=P;}
}
void getinv(long long *f,long long *g,int len){
if(len==1){g[0]=qpow(f[0],P-2);return;}
getinv(f,g,len>>1);
for(int i=0;i<len;++i)H[i]=f[i];for(int i=len;i<len<<1;++i)H[i]=0ll;
for(int i=0;i<len<<1;++i)rev[i]=((rev[i>>1]>>1)|((i&1)*len));
ntt(H,len<<1,1),ntt(g,len<<1,1);
for(int i=0;i<len<<1;++i)H[i]=g[i]*(2ll-H[i]*g[i]%P+P)%P;
ntt(H,len<<1,-1);
for(int i=0;i<len;++i)g[i]=H[i];for(int i=len;i<len<<1;++i)g[i]=0ll;
}
int main(){
scanf("%d",&n);
int len=1;while(len<=n)len<<=1;
fact[0]=1ll;for(int i=1;i<=n;++i)fact[i]=fact[i-1]*i%P;
for(int i=0;i<=n;++i)F[i]=qpow(2ll,((long long)i*(i-1ll)>>1)%(P-1ll))*qpow(fact[i],P-2)%P;
for(int i=1;i<=n;++i)G[i]=qpow(2ll,((long long)i*(i-1ll)>>1)%(P-1ll))*qpow(fact[i-1],P-2)%P;
getinv(F,invF,len);
ntt(invF,len<<1,1),ntt(G,len<<1,1);
for(int i=0;i<len<<1;++i)(G[i]*=invF[i])%=P;
ntt(G,len<<1,-1);
printf("%lld",G[n]*fact[n-1]%P);
}