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