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]=nn2 f [ n ] = n n − 2
对于g,假设不管一条边连或不连都计,那么一共有 n(n1)2 n ∗ ( n − 1 ) 2 条边,这个时候考虑每条边的情况,染成m种颜色或者不存在,那么 g[n]=mn(n1)2 g [ n ] = m n ∗ ( n − 1 ) 2
然后来考虑f。
考虑节点1所在的联通块的大小。那么有

f[n]=i=1n1Ci1n1f[i]g[ni] f [ n ] = ∑ i = 1 n − 1 C n − 1 i − 1 f [ i ] ∗ g [ n − i ]

f[n]=(n1)!i=1n1f[i]g[ni](i1)!(ni)! 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;
}

此处输入图片的描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值