• 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;
    }
  • 相关阅读:
    IOS -- 获取本地图片和网络图片的大小size
    xib中的label加边框
    iOS开发之Masonry框架源码深度解析
    10分钟搭建 App 主流框架
    卸载服务器GitLab
    linux安装git方法
    虚拟机安装centos7, 再安装gitlab 简单步骤
    collectionView 防止cell复用的方法
    UIButton 设置图片文字垂直居中排列
    button获取验证码60秒倒计时 直接用
  • 原文地址:https://www.cnblogs.com/ZH-comld/p/10382595.html
Copyright © 2020-2023  润新知