【BZOJ3456】城市规划

链接:BZOJ3456
(权限题)

题解

首先,点有标号!

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

g(n)=2(n2) g ( n ) = 2 ( n 2 )

可是有啥用呢

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

g(n)=i=1n(n1i1)2(ni   2)f(i) g ( n ) = ∑ i = 1 n ( n − 1 i − 1 ) 2 ( n − i       2 ) f ( i )


2(n2)=i=1n(n1i1)2(ni   2)f(i)=i=1n(n1)!(i1)!(ni)!2(ni   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 )

两边除以 (n1)! ( n − 1 ) !

2(n2)(n1)!=i=1n2(ni   2)f(i)(i1)!(ni)! 2 ( n 2 ) ( n − 1 ) ! = ∑ i = 1 n 2 ( n − i       2 ) f ( i ) ( i − 1 ) ! ( n − i ) !


p(x)=2(x2)x! p ( x ) = 2 ( x 2 ) x !
q(x)=f(x)(x1)! q ( x ) = f ( x ) ( x − 1 ) !

原式变为

2(n2)(n1)!=i=1np(ni)q(i) 2 ( n 2 ) ( n − 1 ) ! = ∑ i = 1 n p ( n − i ) q ( i )

出现卷积。


F(n)=i=1np(i) F ( n ) = ∑ i = 1 n p ( i )
G(n)=i=1nq(i) G ( n ) = ∑ i = 1 n q ( i )

就有

F(n)G(n)=2(n2)(n1)! F ( n ) G ( n ) = 2 ( n 2 ) ( n − 1 ) !


G(n)=2(n2)(n1)!F1 G ( n ) = 2 ( n 2 ) ( n − 1 ) ! F − 1


f(n)=q(n)(n1)! f ( n ) = q ( n ) ( n − 1 ) !
q(n)=[n]G(n) q ( n ) = [ n ] G ( n )

于是得到答案。

时间复杂度 O(NlogN) 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);
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值