1. 程式人生 > >Bus Routes HDU - 5552 [NTT][cayley定理][CDQ分治][計數問題][DP]

Bus Routes HDU - 5552 [NTT][cayley定理][CDQ分治][計數問題][DP]

Bus Routes HDU - 5552

Tags: NTT cayley定理 CDQ分治 計數問題 DP


Bus Routes HDU - 5552

題意

求有n個點的無向帶環聯通圖的m染色方案。

分析

考慮帶環聯通圖其實就是聯通圖總數-樹總數。
而聯通圖總數有是圖減去不聯通圖的數量。
那麼就設
f[n]表示n個點的聯通圖總數
g[n]表示n個點的圖總數
h[n]表示n個點的樹總數

然後g和h的式子比較好想。
對於h,根據cayley定理的應用,可以知道n個有標誌頂點的樹的數目等於

f [ n ] = n n 2
對於g,假設不管一條邊連或不連都計,那麼一共有
(n−1)2" role="presentation"> n ( n 1 ) 2
條邊,這個時候考慮每條邊的情況,染成m種顏色或者不存在,那麼 g [ n ] = m n ( n 1 ) 2
然後來考慮f。
考慮節點1所在的聯通塊的大小。那麼有

f [ n ] = i = 1 n 1 C n 1 i 1 f [ i ] g [ n i ]
f [ n ] = ( n 1 ) ! i = 1 n 1 f [ i ] g [ n i ] ( i 1 ) ! ( n i ) !
就和之前CDQ分治+fft的轉移差不多了。

code

#include<bits/stdc++.h>
#define mo 152076289
#define ll long long
using namespace std;
void read(int &x){
    x=0; char c=getchar();
    for (;c<48;c=getchar());
    for (;c>47;c=getchar())x=(x<<1)+(x<<3)+(c^48);
}
ll ksm(ll a,ll b){
    ll res=1;
    for (;b;b>>=1){
        if (b&1)res=res*a%mo;
        a=a*a%mo;
    }
    return res;
}
#define M 20005
int rev[M];
struct NTT{
    ll a[M];
    ll &operator[](int i){
        return a[i];
    }
    void solve(int n,int DFT){
        register int i,j,k,m;
        ll l,r,w,wn;
        for (i=0;i<n;i++)if (rev[i]<i)swap(a[rev[i]],a[i]);
        for (m=1;m<n;m<<=1){
            k=(m<<1);
            w=1;
            wn=ksm(106,(mo-1)/k);
            if (DFT){
                wn=ksm(wn,mo-2);
            }
            for (i=0;i<m;i++){
                for (j=i;j<n;j+=k){
                    l=a[j]; r=a[j+m];
                    a[j]=(l+w*r%mo)%mo;
                    a[j+m]=(l-w*r%mo+mo)%mo;
                }
                w=w*wn%mo;
            }
        }
        if (DFT){
            ll Chu=ksm(n,mo-2);
            for (i=0;i<n;i++)a[i]=a[i]*Chu%mo;
        }
    }
    void clear(int n){
        for (int i=0;i<n;i++)a[i]=0;
    }
}A,B;
ll f[M],g[M],h[M],jiecheng[M],chu[M];
void solve(int l,int r){
    if (l==r){
        f[l]=(g[l]-jiecheng[l-1]*f[l]%mo+mo)%mo;
        return ;
    }
    int mid=(l+r)>>1,len=r-l+1,i,k;
    solve(l,mid);
    for (k=1;k<len;k<<=1);
    for (i=0;i<k;i++)rev[i]=(rev[i>>1]>>1)|((i&1)*(k>>1));
    A.clear(k); B.clear(k);
    for (i=l;i<=mid;i++){
        A[i-l]=f[i]*chu[i-1]%mo;
    }
    for (i=1;l+i<=r;i++){
        B[i]=g[i]*chu[i]%mo;
    }
    A.solve(k,0); B.solve(k,0);
    for (i=0;i<k;i++)A[i]=(A[i]*B[i])%mo;
    A.solve(k,1);
    for (i=mid+1;i<=r;i++){
        f[i]=(f[i]+A[i-l])%mo;
    }
    solve(mid+1,r);
}
int main(){
//  freopen("1.in","r",stdin);
    int T,n,m,i,Ca;
    jiecheng[0]=1;
    for (i=1;i<M;i++)jiecheng[i]=jiecheng[i-1]*i%mo;
    chu[M-1]=ksm(jiecheng[M-1],mo-2);
    for (i=M-2;i>=0;i--)chu[i]=chu[i+1]*(i+1)%mo;
    read(T);
    for (Ca=1;Ca<=T;Ca++){
        read(n); read(m);
        for (i=1;i<=n;i++){
            f[i]=0;
            g[i]=ksm(m+1,1ll*i*(i-1)/2);
            if (i==1)h[i]=1;
            else h[i]=ksm(i,i-2)*ksm(m,i-1)%mo;
        }
        solve(1,n);
        printf("Case #%d: %lld\n",Ca,(f[n]-h[n]+mo)%mo);
    }   
    return 0;
}

此處輸入圖片的描述