bzoj3456城市规划 多项式取模

題目大意

求出有n个点的有标号简单连通无向图的数目。

题解

什么破玩意,直接输出\(2^{C_n^2}\)走人
我们发现这张图要求连通,而上式肯定不能保证连通。
其实上式表示的是不保证连通的有标号简单无向图。
就差在一个连通上啊。
所以我们设\(f(x)\)表示有x个点的有标号简单连通无向图的数目。
然后设\(g(x)\)为上式,即不保证连通时的方案数
于是我们枚举节点1所在的连通块的大小,有

\[g(n) = \sum_{i=1}^nC_{n-1}^{i-1}f(i)g(n-1) \]

\[\frac{g(n)}{(n-1)!} = \frac{\sum_{i=1}^nC_{n-1}^{i-1}f(i)g(n-1)}{(n-1)!} \]

\[\frac{2^{C_n^2}}{(n-1)!} = \sum_{i=1}{n}\frac{f(i)}{(i-1)!}\frac{2^{C_{n-i}^2}}{(n-i)!} \]

那么我们分别设它们的生成函数为

\[A(x) = \sum_{n=1}^{\infty}\frac{f(n)}{(n-1)!}x^n \]

\[B(x) = \sum_{n=0}^{\infty}\frac{2^{C_n^2}}{n!}x^n \]

\[C(x) = \sum_{n=0}^{\infty}\frac{2^{C_n^2}}{(n-1)!}x^n \]

所以我们有

\[C(x) = A(x)B(x) \]

那么有

\[A(x) \equiv B^{-1}(x)C(x) (mod\text{ }x^{n+1}) \]

所以利用FFT算逆元,处理乘法即可.
//这里用的FNT,即快速数论变换.

#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
    x=0;char ch;bool flag = false;
    while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
    while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
const int maxn = 1 << 18;
const int mod = 1004535809;
const int pri_rt = 3;
inline int qpow(int x,int p){
    int ret = 1;
    for(;p;x=1LL*x*x%mod,p>>=1) if(p&1) ret=1LL*ret*x%mod;
    return ret;
}
int len;
int e[maxn],ie[maxn];
inline int check(int &x){
    if(x < 0) x += mod;
    if(x >= mod) x -= mod;
}
inline void init(){
    int bas = qpow(pri_rt,(mod-1)/len);
    int inv = qpow(bas,mod-2);
    e[0] = ie[0] = 1;
    for(int i=1;i<len;++i){
        e[i] = 1LL*e[i-1]*bas % mod;
        ie[i] = 1LL*ie[i-1]*inv % mod;
    }
}
void FNT(int n,int *x,int *w){
    for(int i=0,t=0;i<n;++i){
        if(i > t) swap(x[i],x[t]);
        for(int j=n>>1;(t^=j) < j;j>>=1);
    }
    for(int m=2;m<=n;m<<=1){
        int k = m>>1,wn = len/m;
        for(int i=0;i<n;i+=m){
            for(int j=0,p=0;j<k;++j,p+=wn){
                int u = 1LL*x[i+j+k]*w[p] % mod;
                x[i+j+k] = x[i+j] - u;check(x[i+j+k]);
                x[i+j] = x[i+j] + u;check(x[i+j]);
            }
        }
    }
    if(w == ie){
        int inv = qpow(n,mod-2);
        for(int i=0;i<n;++i) x[i] = 1LL*x[i]*inv%mod;
    }
}
struct Node{
    int n;
    int x[maxn];
    Node(){n = x[0] = 0;}
};
void get_inv(int n,const Node &A,Node &B){
    if(n == 1){
        B.n = 1;
        B.x[0] = qpow(A.x[0],mod-2);
        return;
    }
    static int X[maxn];
    get_inv((n+1)>>1,A,B);
    int p = 1;for(;p < n<<1 ;p<<=1);
    copy(A.x,A.x+n,X);fill(X+n,X+p,0);
    fill(B.x+B.n,B.x+p,0);
    FNT(p,X,e);FNT(p,B.x,e);
    for(int i=0;i<p;++i) B.x[i] = 1LL*B.x[i]*(2 - 1LL*X[i]*B.x[i] % mod) % mod;
    FNT(p,B.x,ie);B.n = n;
}
Node A,B,C;
int inv[maxn],inv_fac[maxn],c[maxn];
int main(){
    int n;read(n);
    int p;for(p=1;p<(n+1)<<1;p<<=1);
    len = p;init();
    inv[1] = inv_fac[0] = 1;
    for(int i=1;i<=n;++i){
        if(i != 1) inv[i] = -mod/i*(ll)inv[mod % i] % mod;
        if(inv[i] < 0) inv[i] += mod;
        inv_fac[i] = (ll)inv_fac[i-1]*inv[i]%mod;
    }
    c[0] = c[1] = 1;
    for(int i=2;i<=n;++i) c[i] = qpow(2,(ll)i*(i-1)/2 % (mod - 1));
    A.n = B.n = n+1;
    for(int i=0;i<=n;++i) A.x[i] = (ll)c[i]*inv_fac[i] % mod;
    for(int i=1;i<=n;++i) B.x[i] = (ll)c[i]*inv_fac[i-1]%mod;
    get_inv(n+1,A,C);
    fill(C.x + C.n,C.x + p,0);
    FNT(p,C.x,e);FNT(p,B.x,e);
    for(int i=0;i<=p;++i) C.x[i] = (ll)C.x[i]*B.x[i] % mod;
    FNT(p,C.x,ie);
    int ans = (ll)C.x[n]*qpow(inv_fac[n-1],mod - 2) % mod;
    if(ans < 0) ans += mod;
    printf("%d\n",ans);
    getchar();getchar();
    return 0;
}
posted @ 2017-02-13 15:04  Sky_miner  阅读(967)  评论(0编辑  收藏  举报