題目
題目大意:給定一個n個節點的樹,給它染色並且使得相鄰節點異色。問恰好用k種顏色的染色方案數
題目分析
恰好k種不是很好求,因爲我們很難保證每種顏色都用到,於是我們先考慮求最多k種顏色。
那麼就讓每個點和它的父親節點異色就可以了。
也就是
那麼我們令表示最多用i種顏色的染色方案數
表示恰好用i種的染色方案數
那麼假設我們要最多用n種,那麼不妨從中隨意選其中i種,用且僅用這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;
}
總結
關於二項式反演,其實就是在恰好、最多(少)之間做轉換以達到簡化問題的作用。
最難的一步就是找到反演的原式子。
用到了“算兩次”的思想,用難算的表示好算的,然後用反演。
其實反演就是容斥,只不過有的時候直接用容斥不太好想。