Kings Colors-二項式反演

題目

題目鏈接

題目大意:給定一個n個節點的樹,給它染色並且使得相鄰節點異色。問恰好用k種顏色的染色方案數

題目分析

恰好k種不是很好求,因爲我們很難保證每種顏色都用到,於是我們先考慮求最多k種顏色。

那麼就讓每個點和它的父親節點異色就可以了。

也就是k(k1)n1k*(k-1)^{n-1}

那麼我們令f(i)f(i)表示最多用i種顏色的染色方案數

g(i)g(i)表示恰好用i種的染色方案數

那麼假設我們要最多用n種,那麼不妨從中隨意選其中i種,用且僅用這i種顏色染色。

也就是說f(n)=i=1nCnig(i)f(n)=\sum_{i=1}^{n}{C_{n}^{i}g(i)}

然後就要用到二項式反演

g(n)=i=1n(1)niCnif(i)g(n)=\sum_{i=1}^{n}{(-1)^{n-i}C_{n}^{i}f(i)}

f之前求出來了。問題就解決了。

AC代碼

#include<bits/stdc++.h>
using namespace std;
int read(){
	char s;
	int x=0,f=1;
	s=getchar();
	while(s<'0'||s>'9'){
		if(s=='-')f=-1;
		s=getchar();
	}
	while(s>='0'&&s<='9'){
		x*=10;
		x+=s-'0';
		s=getchar();
	}
	return x*f;
}
const long long mod=1000000007;
const int N=2600;
long long qpow(long long a,long long b){
	if(b==0)return 1;
	long long rec=qpow(a,b/2);
	if(b&1)return rec*rec%mod*a%mod;
	return rec*rec%mod;
}
int n,k;
long long f[N];//最多用i種顏色的方案數 
long long calc[N],inv[N];
void init(){
	calc[0]=1;
	for(int i=1;i<=n;i++){
		f[i]=i*qpow((long long)i-1,(long long)n-1)%mod;
		calc[i]=(long long)calc[i-1]*i%mod;
	}
	inv[n]=qpow(calc[n],mod-2);
	for(int i=n-1;i>=0;i--){
		inv[i]=(long long)inv[i+1]*(i+1)%mod;
	}
}
long long C(int i,int j){
	long long sum=calc[i]*inv[j]%mod*inv[i-j]%mod;
	return sum;
}
int main(){
	n=read(),k=read();
	init();
	long long ans=0;
	long long id=1;
	for(int i=k;i>=1;i--){
		ans=(ans+id*f[i]%mod*C(k,i)%mod)%mod;
		ans%=mod;
		id=-id;
	}
	ans=(ans%mod+mod)%mod;
	printf("%lld\n",ans);
	return 0;
}

總結

關於二項式反演,其實就是在恰好、最多(少)之間做轉換以達到簡化問題的作用。

最難的一步就是找到反演的原式子。

用到了“算兩次”的思想,用難算的表示好算的,然後用反演。

其實反演就是容斥,只不過有的時候直接用容斥不太好想。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章