HDU5552 Bus Routes(分治NTT)
HDU5552 Bus Routes
题目大意
有 n n n个点, m m m种颜色,将 n n n个点连接起来,要求得到一个有环的连通图(没有重边和自环),同时可以对每条边染色。求有多少种方案?
输出答案模 152076289 152076289 152076289后的值。
有 T T T组数据。
1 < n ≤ 1 0 4 , 0 < m < 2 31 , T ≤ 200 1< n\leq 10^4,0< m< 2^{31},T\leq 200 1<n≤104,0<m<231,T≤200
题解
前置知识:分治FFT(NTT)
设 f ( n ) f(n) f(n)表示 n n n个点的连通图的染色方案总数, g ( n ) g(n) g(n)表示 n n n个点的图的染色方案总数, h ( n ) h(n) h(n)表示 n n n个点连接的树的染色方案总数,则
g ( n ) = ( m + 1 ) n ( n − 1 ) 2 g(n)=(m+1)^{\frac{n(n-1)}{2}} g(n)=(m+1)2n(n−1)
g ( n ) g(n) g(n)的式子表示任意连边,连的边任意染色,不连的不染色。
h ( n ) = n n − 2 × m n − 1 h(n)=n^{n-2}\times m^{n-1} h(n)=nn−2×mn−1
h ( n ) h(n) h(n)的式子表示构成树方案的 n n − 2 n^{n-2} nn−2, n − 1 n-1 n−1条边任意染色就乘 m n − 1 m^{n-1} mn−1。
f ( n ) = g ( n ) − ∑ i = 1 n − 1 C n − 1 i − 1 × f ( i ) × g ( n − i ) = g ( n ) − ( n − 1 ) ! ∑ i = 1 n − 1 f ( i ) ( i − 1 ) ! g ( n − i ) ( n − i ) ! f(n)=g(n)-\sum\limits_{i=1}^{n-1}C_{n-1}^{i-1}\times f(i)\times g(n-i)=g(n)-(n-1)!\sum\limits_{i=1}^{n-1}\dfrac{f(i)}{(i-1)!}\dfrac{g(n-i)}{(n-i)!} f(n)=g(n)−i=1∑n−1Cn−1i−1×f(i)×g(n−i)=g(n)−(n−1)!i=1∑n−1(i−1)!f(i)(n−i)!g(n−i)
枚举第一个点所在的连通块即可推出 f ( n ) f(n) f(n)的转移式。
用分治 N T T NTT NTT来解决,最后的答案为 f ( n ) − h ( n ) f(n)-h(n) f(n)−h(n)。
时间复杂度为 O ( T n log 2 n ) O(Tn\log^2 n) O(Tnlog2n)。
code
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int N=30000;
const long long G=106,mod=152076289;
long long ans,tmp,f[30005],g[30005],a1[30005],a2[30005];
long long jc[30005],ny[30005];
long long in(){
long long re=0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9'){
re=re*10+ch-'0';
ch=getchar();
}
return re;
}
long long mi(long long t,long long v){
long long re=1;
while(v){
if(v&1) re=re*t%mod;
t=t*t%mod;
v/=2;
}
return re;
}
void init(){
jc[0]=1;
for(int i=1;i<=N;i++) jc[i]=jc[i-1]*i%mod;
ny[N]=mi(jc[N],mod-2);
for(int i=N-1;i>=0;i--) ny[i]=ny[i+1]*(i+1)%mod;
}
void ch(long long *a,int l){
for(int i=1,j=l/2,k;i<l-1;i++){
if(i<j) swap(a[i],a[j]);
k=l/2;
while(j>=k){
j-=k;k>>=1;
}
j+=k;
}
}
void ntt(long long *a,int l,int fl){
long long wn,w;
for(int i=2;i<=l;i<<=1){
if(fl==1) wn=mi(G,(mod-1)/i);
else wn=mi(G,mod-1-(mod-1)/i);
for(int j=0;j<l;j+=i){
w=1;
for(int k=j;k<j+i/2;k++,w=w*wn%mod){
long long t=a[k],u=w*a[k+i/2]%mod;
a[k]=(t+u)%mod;
a[k+i/2]=(t-u+mod)%mod;
}
}
}
if(fl==-1){
long long ny=mi(l,mod-2);
for(int i=0;i<l;i++) a[i]=a[i]*ny%mod;
}
}
void solve(int l,int r){
if(l==r){
f[l]=(f[l]+g[l])%mod;
return;
}
int mid=l+r>>1;
solve(l,mid);
int len=1;
while(len<r-l+1) len<<=1;
for(int i=0;i<len;i++) a1[i]=a2[i]=0;
for(int i=0;i<=mid-l;i++) a1[i]=f[i+l]*ny[i+l-1]%mod;
for(int i=0;i<=r-l;i++) a2[i]=g[i]*ny[i]%mod;
ch(a1,len);ch(a2,len);
ntt(a1,len,1);ntt(a2,len,1);
for(int i=0;i<len;i++){
a1[i]=a1[i]*a2[i]%mod;
}
ch(a1,len);
ntt(a1,len,-1);
for(int i=mid+1;i<=r;i++){
f[i]=(f[i]-jc[i-1]*a1[i-l]%mod+mod)%mod;
}
solve(mid+1,r);
}
int main()
{
init();
int t,n,m;
t=in();
for(int o=1;o<=t;o++){
n=in();m=in();
memset(f,0,sizeof(f));
g[0]=1;
for(int i=1;i<=n;i++) g[i]=mi(m+1,i*(i-1)/2);
solve(1,n+1);
ans=f[n];
tmp=mi(n,n-2)*mi(m,n-1)%mod;
ans=(ans-tmp+mod)%mod;
printf("Case #%d: %lld\n",o,ans);
}
return 0;
}