题目:
题意:
大致是一共有 M 中颜色,你需要从中选 K 种给 N 个物品染色,要求相邻的不能染一样的颜色,并且每种颜色都要用上,求染色的方案数
笔记:
学习自:容斥原理详解
容斥公式:
先看这个问题:K 种颜色给 N 个物品染色,要求相邻的不能染一样的颜色的方案数,答案就是:k*pow(k-1,n-1),此时算的是小于等于 k 个颜色的总方案数;容斥原理的套路就是考虑其逆问题:至少有一种颜色不用;设 A1 为不用第一种颜色的方案数,A2 为不用第二种颜色的方案数......Ak 为不用第 k 种颜色的的集合,逆问题的方案数就是 Ai 的并集,这个就可以通过容斥公式计算;那么最后的答案就是: (总方案数 - 逆问题的方案数) * m 种颜色中选 k 种颜色的方案数
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 1e9+7;
const int maxn = 1e6+16;
int inv[maxn],T,n,m,k;
LL qpow(LL a,LL x){
LL res = 1;
while(x){
if(x&1) res = res*a%mod;
a = a*a%mod;
x >>= 1;
}
return res;
}
inline LL cal(int n,int k){
return k*qpow(k-1,n-1)%mod;
}
void init(){ //预处理逆元
inv[1] = 1;
for(int i = 2;i < maxn; ++i)
inv[i] = 1ll*(mod-mod/i)*inv[mod%i]%mod;
}
LL solve(int n,int k){ //容斥公式算逆问题的方案数
LL res = 0; LL v = 1;
for(int i = 1;i <= k-2; ++i){
v = v*(k-i+1)%mod*inv[i]%mod;
if(i&1) res += v*cal(n,k-i);
else res -= v*cal(n,k-i);
res = (res%mod+mod)%mod;
}
return res;
}
LL C(int m,int k){ //递推求组合数
LL res = 1;
for(int i = 1;i <= k; ++i)
res = res*(m-i+1)%mod*inv[i]%mod;
return res;
}
int main(){
scanf("%d",&T); init();
for(int Case = 1;Case <= T; ++Case){
scanf("%d %d %d",&n,&m,&k);
printf("Case #%d: %d\n",Case,(int)(C(m,k)*(cal(n,k)-solve(n,k)+mod)%mod));
}
return 0;
}