UVALive - 7251 2015 ICPC Hefei

题目链接:UVALive - 7251

题意:

统计带标号 n 个点可以组成多少种不同的有环连通图,答案
对质数 ( 2 15 × 4641 + 1 ) (2^{15} × 4641 + 1) (215×4641+1) 取模,其中每条边可以染成 m m m 种颜

sol:

考虑算出联通图的方案数,再减去树的方案个数。

1. n n n个点树的方案个数为 n n − 2 n^{n-2} nn2
2.考虑任意图的指数生成函数
G ( x ) = ∑ n ≥ 0 ( m + 1 ) ( n 2 ) n ! x n G(x) = \sum_{n \ge 0} \frac {(m+1)^{\binom{n}{2}}}{n!} x^n G(x)=n0n!(m+1)(2n)xn,即对所有的边考虑染色.
3. 设连通图的指数生成函数为 F ( x ) F(x) F(x)
G ( x ) = ∑ k ≥ 1 F k ( x ) k ! = exp ⁡ ( F ( x ) ) G(x) = \sum_{k \ge1} \frac{F_k(x)}{k!} = \exp(F(x)) G(x)=k1k!Fk(x)=exp(F(x))
4.对 G ( x ) G(x) G(x)求对数即可,详情见 2015 2015 2015金策论文

code:

#include

using namespace std;
typedef long long ll;

const int maxn = 4e5+10;
//不声明为常量慢一倍
const int P = 152076289;
const int mod = 152076289;
const ll g = 127;

void Add(ll& x,ll y){
    x += y;
    if(x>=mod) x-= mod;
}

void Mul(ll& x,ll y){
    x *= y;
    if(x>=mod) x%=mod;
}

ll qpow(ll a,ll b){
    ll ret = 1;
    while(b){
        if(b&1) Mul(ret,a);
        b >>= 1;
        Mul(a,a);
    }
    return ret;
}

inline ll Inv(ll a){
    return qpow(a,mod-2);
}

ll inv[maxn],fac[maxn],finv[maxn];

void init(){
    inv[0] = inv[1] = fac[0] = fac[1] = 1;
    for(int i = 2;i<maxn;i++) inv[i] = inv[P % i] * (P - P/i) % P;
    for(int i = 2;i<maxn;i++) fac[i] = fac[i - 1] * i % mod;
    finv[0] = finv[1] = 1;
    for(int i = 2;i<maxn;i++) finv[i] = finv[i-1] * inv[i] % mod;
}

struct NTT{
    int rev[maxn],N,L;

    void init_rev(int n){
        for(N = 1,L=0;N<=n;N<<=1,L++);
        for(int i=1;i<N;i++) rev[i] = (rev[i >> 1] >> 1 | ((i&1) << (L-1)));
    }

    void DFT(ll a[],int flag){
        for(int i = 0;i<N;i++)
            if(i<rev[i]) swap(a[i],a[rev[i]]);
        for(int l = 2;l<=N;l <<=1 ){
            ll wn;
            if(flag == 1) wn = qpow(g,(P-1)/l);
            else wn = qpow(g,P-1-(P-1)/l);
            for(int k = 0;k<N;k += l){
                ll w = 1;
                ll x,y;
                for(int j = k;j < k + l/2 ;j++){
                    x = a[j];
                    y = a[j + l/2] * w % P;
                    a[j] = (x + y) % P;
                    a[j + l/2] = (x - y +P) % P;
                    Mul(w,wn);
                }
            }
        }
        if(flag == -1){
            ll inv = Inv(N);
            for(int i = 0; i<N ;i++) a[i] = a[i] * inv % P;
        }
    }
}ntt;

ll A[maxn],B[maxn],C[maxn],D[maxn];
ll f[maxn],G[maxn];

void poly_inv(int deg,ll* a,ll * b){
    if(deg == 1){
        b[0] = Inv(a[0]);
        return ;
    }
    poly_inv((deg+1)>>1,a,b); ntt.init_rev(deg<<1);
    for(int i = 0;i<deg;i++) C[i] = a[i];
    for(int i = deg;i<ntt.N;i++) C[i] = b[i] = 0;
    ntt.DFT(C,1); ntt.DFT(b,1);
    for(int i = 0;i<ntt.N;i++) 
        b[i] = ((2LL - C[i] * b[i] % P) + P) % P * b[i] % P;
    ntt.DFT(b,-1);
    for(int i = deg;i<ntt.N;i++) b[i] = 0;
}

void Direv(int n,ll* A,ll* B,int flag){
    if(flag == 1){
        for(int i = 1;i<n;i++) B[i-1] = A[i] * i % mod;
        B[n - 1] = 0;
    }
    else{
        for(int i = 1;i<n;i++) B[i] = A[i-1] * inv[i] % mod;
        B[0] = 0;
    }
}

void get_log(int n,ll* a,ll* b){
    Direv(n,a,A,1);
    poly_inv(n,a,B);
    ntt.DFT(A,1); ntt.DFT(B,1);
    for(int i = 0;i<ntt.N;i++) A[i] = A[i] * B[i] % mod;
    ntt.DFT(A,-1);
    Direv(n,A,b,-1);
    for(int i = 0;i<ntt.N;i++) A[i] = B[i] = 0;
    for(int i = n;i<ntt.N;i++) b[i] = 0;
}

int main(){
    init();
    int T;
    scanf("%d",&T);
    for(int cas = 1;cas<=T;cas++){
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i = 0;i <= n + 100;i++){
            ll c2 = (ll)i * (i-1) / 2;
            f[i] = qpow(m+1,c2);
            // f[i] = f[i] * Inv(fac[i]) % mod;
            // if(i<10) cout<
            f[i] = f[i] * finv[i] % mod;
        }
        // puts("");
        get_log(n+10,f,G);
        ll ans = G[n] * fac[n] % mod;
        ll ret = 0;
        if(n >= 2) ret = qpow(n,n-2) * qpow(m,n-1) % mod;
        ans = (ans + mod - ret) % mod;
        printf("Case #%d: %lld\n",cas,ans);
    }
    return 0;
}

你可能感兴趣的:(fft&&ntt,组合数学,多项式,生成函数)