zoukankan      html  css  js  c++  java
  • BZOJ 3456: 城市规划 多项式求逆

    Description

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

    Input

     仅一行一个整数n(<=130000)

    Output

     仅一行一个整数, 为方案数 mod 1004535809.

    题解:

    多项式好有趣
     
    我们先考虑递推式:

    令 $f(n)$ 代表 $n$ 个点简单联通无向图个数.

    令 $g(n)$ 代表 $n$ 个点无向图个数 (不要求联通).

    不难求出,$g(n)=frac{n(n-1)}{2}$,即 $inom{n}{2}$.

    然而,$f(n)$ 似乎特别难求.

    枚举 $1$ 号点所在的联通块:
     
    得 $g(n)=sum_{i=1}^{n}inom{n-1}{i-1}f(i)g(n-i)$
     
    思考一下该公式代表的含义:

    枚举 $1$ 号点所在的联通块大小,其余的点随便连,如何保证不会重复枚举?

    每一次 $1$ 号点所在的联通块大小是固定的.

    其余的点之间不能连到 $1$ 号点所在联通块中,这样就满足了要求.
     
    把 $g(n)$ 带入, 得:
     
    $2^{inom{n}{2}}=sum_{i=1}^{n}inom{n-1}{i-1}f(i)2^{inom{n-i}{2}}$
     
    把组合数展开,得:
     
    $2^{inom{n}{2}}=sum_{i=1}^{n}frac{(n-1)!}{(i-1)!(n-i)!}f(i)2^{inom{n-i}{2}}$
     
    $(n-1)!$ 似乎有点碍事,除掉!!
     
    $frac{2^{inom{n}{2}}}{(n-1)!}=sum_{i=1}^{n}frac{f(i)2^{inom{n-i}{2}}}{(i-1)!(n-i)!}$
     
    看起来像不像卷积 ??

    ——似乎不太像.
     
    不急不急QAQ,再定义几个函数:

    定义 $F(x)=sum_{i=1}^{infty}frac{2^{inom{i}{2}}}{(i-1)!}$
     
    定义 $G(x)=sum_{i=1}^{infty}frac{f(i)}{(i-1)!}$

    定义 $H(x)=sum_{i=0}^{infty}frac{2^{inom{i}{2}}}{i!}$
     
    令函数 $X_{i}$ 表示 多项式 $X$ 的第 $i$ 项系数.
     
    观察 $frac{2^{inom{n}{2}}}{(n-1)!}=sum_{i=1}^{n}frac{f(i)2^{inom{n-i}{2}}}{(i-1)!(n-i)!}$
     
    得出: $F_{n}=sum_{i=1}^{n}G_{i}H_{n-i}$
     
    现在,能看出这个是卷积了吧!
     
    即 $F(x)=G(x)H(x)$
     
    我们要求的是 $G(x)$,则 $G(x)=F(x)H(x)^{-1}$.
     
    来一波多项式求逆即可.  
     

    Code:

    #include <bits/stdc++.h>
    #define N 3333300
    #define mod 1004535809 
    #define ll long long 
    #define setIO(s) freopen(s".in","r",stdin) 
    #define shut fclose(stdin),fclose(stdout) 
    using namespace std;
    int qpow(int x,int y)
    {
        int tmp = 1;
        while(y)
        {
            if(y&1) tmp=1ll*tmp*x%mod; 
            x=1ll*x*x%mod,y>>=1; 
        }
        return tmp; 
    }
    namespace NTT
    {
        int a[N],b[N],f[N],g[N]; 
        void NTT(int *a,int len,int opt){ 
            for(int i = 0,k = 0;i < len; ++i)
            {
                if(i > k) swap(a[i],a[k]); 
                for(int j = len >> 1;(k^=j)<j;j>>=1); 
            }
            for(int k = 2;k <= len;k <<= 1)
            {
                int t = (k>>1),x=qpow(3,(mod-1)/k); 
                if(opt==-1) x=qpow(x,mod-2); 
                for(int i=0;i<len;i+=k)
                {
                    int w=1; 
                    for(int j=i;j<i+t;++j)
                    {
                        int tmp=1ll*a[j+t]*w%mod; 
                        a[j+t]=(a[j]-tmp+mod)%mod; 
                        a[j]=(a[j]+tmp)%mod; 
                        w=1ll*w*x%mod; 
                    }
                }
            }
            if(opt==-1) for(int i=0,t=qpow(len,mod-2);i<len;++i) a[i]=1ll*a[i]*t%mod; 
        }
        void solve(int *a,int *b,int len)
        {      
            if(len==1) { b[0]=qpow(a[0],mod-2); return ; }
            solve(a,b,len>>1);       
            for(int i=0;i<len;++i) f[i]=a[i],g[i]=b[i]; 
            NTT(f,len<<1,1),NTT(g,len<<1,1); 
            for(int i=0;i<(len<<1);++i) f[i]=((1ll*f[i]*g[i])%mod*g[i])%mod; 
            NTT(f,len<<1,-1); 
            for(int i=0;i<len;++i)  b[i]=((b[i]<<1)%mod-f[i]+mod)%mod; 
        }
        void Inv(int *a,int *b,int len)
        {
            int m = 1;
            while(m <= len) m <<= 1; 
            solve(a,b,m); 
        }
    };
    int inv[N],jc[N],jv[N]; 
    int p[N],G[N],C[N],F[N],D[N]; 
    int main()
    {
        // setIO("input"); 
        int n,m; 
        scanf("%d",&n); 
        for(m = 1;m <= n;m <<= 1);       
        inv[0] = jc[0] = inv[1] = jc[1] = jv[0] = jv[1] = 1; 
        for(int i=2;i<=n;++i)
        {
            jc[i]=1ll*jc[i-1]*i%mod; 
            inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod; 
            jv[i]=1ll*jv[i-1]*inv[i]%mod; 
        }
        p[0]=p[1]=1; 
        for(int i=2;i<=n;++i) p[i]=qpow(2,1ll*(i-1)*i/2%(mod-1));     
        for(int i=0;i<=n;++i) G[i]=1ll*p[i]*jv[i]%mod; 
        for(int i=1;i<=n;++i) C[i]=1ll*p[i]*jv[i-1]%mod; 
        NTT::solve(G,D,m); 
        NTT::NTT(D,m,1);NTT::NTT(C,m,1); 
        for(int i=0;i<m;++i) F[i]=1ll*D[i]*C[i]%mod; 
        NTT::NTT(F,m,-1); 
        printf("%lld
    ",1ll*F[n]*jc[n-1]%mod); 
        return 0; 
    }
    

      

  • 相关阅读:
    poj 1789 Truck History(最小生成树)
    POJ 3096 Surprising Strings(STL map string set vector)
    hdu 1412 (STL list)
    POJ 1552 Doubles (C++ STL set使用)
    poj 水题系列
    洛谷P4859 已经没有什么好害怕的了
    CF1228E Another Filling the Grid
    二项式反演
    AT [ABC177F] I hate Shortest Path Problem
    [NOI2020]制作菜品
  • 原文地址:https://www.cnblogs.com/guangheli/p/10935598.html
Copyright © 2011-2022 走看看