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<n104,0<m<231,T200


题解

前置知识:分治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(n1)

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)=nn2×mn1

h ( n ) h(n) h(n)的式子表示构成树方案的 n n − 2 n^{n-2} nn2 n − 1 n-1 n1条边任意染色就乘 m n − 1 m^{n-1} mn1

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=1n1Cn1i1×f(i)×g(ni)=g(n)(n1)!i=1n1(i1)!f(i)(ni)!g(ni)

枚举第一个点所在的连通块即可推出 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;
}
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值