【BZOJ3456】城市规划

链接:BZOJ3456
(权限题)

题解

首先,点有标号!

f ( n ) 表示 n 个点组成的有标号连通无向图个数, g ( n ) 表示 n 个点组成的有标号无向图个数(不要求连通)。那这个 g ( n ) 谁都会啦

g ( n ) = 2 ( n 2 )

可是有啥用呢

一个图是由若干强联通分量组成,令 i 1 节点所在强联通分量的大小,可以得到 g ( n ) 的又一表达式

g ( n ) = i = 1 n ( n 1 i 1 ) 2 ( n i       2 ) f ( i )
2 ( n 2 ) = i = 1 n ( n 1 i 1 ) 2 ( n i       2 ) f ( i ) = i = 1 n ( n 1 ) ! ( i 1 ) ! ( n i ) ! 2 ( n i       2 ) f ( i )
两边除以 ( n 1 ) !
2 ( n 2 ) ( n 1 ) ! = i = 1 n 2 ( n i       2 ) f ( i ) ( i 1 ) ! ( n i ) !
p ( x ) = 2 ( x 2 ) x !
q ( x ) = f ( x ) ( x 1 ) !
原式变为
2 ( n 2 ) ( n 1 ) ! = i = 1 n p ( n i ) q ( i )
出现卷积。

F ( n ) = i = 1 n p ( i )
G ( n ) = i = 1 n q ( i )
就有
F ( n ) G ( n ) = 2 ( n 2 ) ( n 1 ) !
G ( n ) = 2 ( n 2 ) ( n 1 ) ! F 1
f ( n ) = q ( n ) ( n 1 ) !
q ( n ) = [ n ] G ( n )
于是得到答案。

时间复杂度 O ( N log N )

代码

#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);
}

猜你喜欢

转载自blog.csdn.net/ezoixx174/article/details/81607262