BZOJ3456城市规划

题目描述

刚刚解决完电力网络的问题, 阿狸又被领导的任务给难住了.
刚才说过, 阿狸的国家有n个城市, 现在国家需要在某些城市对之间建立一些贸易路线, 使得整个国家的任意两个城市都直接或间接的连通.
为了省钱, 每两个城市之间最多只能有一条直接的贸易路径. 对于两个建立路线的方案, 如果存在一个城市对, 在两个方案中是否建立路线不一样, 那么这两个方案就是不同的, 否则就是相同的. 现在你需要求出一共有多少不同的方案.
好了, 这就是困扰阿狸的问题. 换句话说, 你需要求出n个点的简单(无重边无自环)无向连通图数目.
由于这个数字可能非常大, 你只需要输出方案数mod 1004535809(479 * 2 ^ 21 + 1)即可.

题解

其实就是无向连通图计数问题。

都给了这个模数了,肯定要FFT做。

考虑定义生成函数F表示n个点的连通图个数。

G表示n个点的无向图个数,各一发现,G[i]=2C(n,2)

然后考虑dp出G来。

G[n]=∑F[i]*G[n-i]*C(n-1,i-1)

这里的i枚举的是和1点连通的点的个数。

然后看到组合数就得想一想怎么把它拆开。

G[n]=∑F[i]*G[n-i]*(n-1)!*(1/(i-1)!)*(1/(n-i)!)

把右边的(n-1)!移到左边去。

G[n]/(n-1)!=∑F[i]/(i-1)!*G[n-i]/(n-i)!

于是这个式子变成可卷积的形式。

那么我们令

C=F[i]/(i-1)!  

Q=2C(n,2)/n!

Q'=2C(n,2)/(n-1)!

那么Q'=Q*C

C=Q*Q'-1

求一个逆就好了。

注意:当我们预处理多项式的时候,遇到一些奇怪的式子的时候,要想一想它的实际意义。

例如G[0]表示0个点的答案显然为1,G[1]同理也是1,但按照式子算的话为0,这时应从实际的角度计算。

C[0]中有一项为(-1)!但是并没有找到什么和实际有关的,所以我们认为这一项为0。

代码

#include<iostream>
#include<cstdio>
#define N 550002
using namespace std;
typedef long long ll;
ll g[N],a[N],b[N],rev[N],c[N],n,jie[N],ni[N],yu[N];
const ll mod=1004535809;
const ll G=3;
const ll Gi=334845270;
inline int rd(){
    int x=0;char c=getchar();bool f=0;
    while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return f?-x:x; 
}
inline ll power(ll x,ll y){
    ll ans=1;
    while(y){if(y&1)ans=ans*x%mod;x=x*x%mod;y>>=1;}
    return ans;
} 
inline void NTT(ll *a,int l,int tag){
    for(int i=1;i<l;++i)if(i>rev[i])swap(a[i],a[rev[i]]);
    for(int i=1;i<l;i<<=1){
        ll wn=power(tag==1?G:Gi,(mod-1)/(i<<1));
        for(int j=0;j<l;j+=(i<<1)){
            ll w=1;
            for(int k=0;k<i;++k,w=w*wn%mod){
                ll x=a[j+k],y=a[i+j+k]*w%mod;
                a[j+k]=(x+y)%mod;;a[i+j+k]=(x-y+mod)%mod;
            } 
        }
    }
}
inline void inv(int n){
    if(n==1){b[0]=power(g[0],mod-2);return;}
    inv((n+1)>>1);
    int l=1,L=0;
    while(l<=(n<<1))l<<=1,L++;
    for(int i=1;i<l;++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1));
    for(int i=0;i<n;++i)a[i]=g[i];
    for(int i=n;i<l;++i)a[i]=0;
    NTT(a,l,1);NTT(b,l,1);
    for(int i=0;i<l;++i)b[i]=(2ll-a[i]*b[i]%mod+mod)%mod*b[i]%mod;
    NTT(b,l,-1);ll ny=power(l,mod-2);
    for(int i=0;i<n;++i)b[i]=b[i]*ny%mod;
    for(int i=n;i<l;++i)b[i]=0; 
}
inline ll C(ll n){return n*(n-1)/2;}
int main(){ 
    n=rd();
    jie[0]=1;
    for(int i=1;i<=n;++i)jie[i]=jie[i-1]*i%mod;ni[n]=power(jie[n],mod-2);
    for(int i=n-1;i>=0;--i)ni[i]=ni[i+1]*(i+1)%mod;
    yu[0]=yu[1]=1;
    for(int i=2;i<=n;++i)yu[i]=power(2,C(i));
    g[0]=yu[0]*ni[0]%mod;
    for(int i=1;i<=n;++i){
        c[i]=yu[i]*ni[i-1]%mod;
        g[i]=yu[i]*ni[i]%mod;
    }
//    for(int i=0;i<=n;++i)cout<<c[i]<<" ";puts("");
//    for(int i=0;i<=n;++i)cout<<g[i]<<" ";puts("");
    inv(n+1);
    int l=1,L=0;
    while(l<=(n<<1))l<<=1,L++; 
    for(int i=1;i<l;++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1));
//    for(int i=0;i<l;++i)cout<<b[i]<<" ";cout<<endl;
    NTT(c,l,1);NTT(b,l,1);
    for(int i=0;i<l;++i)c[i]=c[i]*b[i]%mod;
    NTT(c,l,-1);ll ny=power(l,mod-2);
    for(int i=0;i<l;++i)c[i]=c[i]*ny%mod;
    cout<<c[n]*jie[n-1]%mod;
    return 0;
}
posted @ 2019-02-15 11:18  comld  阅读(202)  评论(0编辑  收藏  举报