题目链接:https://vjudge.net/problem/Gym-100548F
题目大意:
n 朵花,按顺序排成一排。从 m 种颜色中选出 k 种颜色,给这 n 朵花染色,要求相邻的花颜色不同。问共有多少种染色方案?
((1 le n,m le 10^{9}, 1 le k le 10^{6}, k le n,m))
知识点: 快速幂算法、组合数、容斥原理、逆元。
解题思路:
第一步:从 m 种颜色中选出 k 种颜色,方案数:(C_m^k);
第二步:将这 k 种颜色合理地安排到每一朵花上,要求每一种颜色都有用到,而且相邻的花颜色不同。在此我们设 (G(x)) 为将 x 种颜色合理地安排到每一朵花上,只要求相邻的花颜色不同,不要求每一种颜色都有用到。则 ( G(x) = x(x-1)^{n-1}) 。根据容斥原理,我们可得第二步的方案数为:
( C_{k}^{k}G(k) - C_{k}^{k-1}G(k-1) + ... +(-1)^{k-2}G(2));
则总的方案数为:
(C_m^k imes[C_{k}^{k}G(k) - C_{k}^{k-1}G(k-1) + ... +(-1)^{k-2}G(2)])
(=C_m^k imes[C_{k}^{k}k(k-1)^{n-1} - C_{k}^{k-1}(k-1)(k-2)^{n-1} + ... +(-1)^{k-2} imes2]).
AC代码:
1 #include<cstdio> 2 using namespace std; 3 typedef long long ll; 4 const ll mod = 1e9 + 7; 5 const int maxn = 1e6 + 5; 6 ll inv[maxn], C_k[maxn]; 7 8 ll exp_mod(ll a, ll b) { //快速幂求a^b%mod 9 ll ret = 1; 10 while (b) { 11 if (b & 1) ret = (ret*a) % mod; 12 a = (a*a) % mod; 13 b >>= 1; 14 }return ret; 15 } 16 void init() { //逆元表 17 inv[1] = 1; 18 for (int i = 2; i<maxn; i++) 19 inv[i] = exp_mod((ll)i, mod - 2) % mod; 20 } 21 void find_Ck(ll k) { //求出C(K, 0,1,...k) 22 C_k[0] = 1; 23 for (ll i = 1; i <= k; i++) { 24 C_k[i] = ((C_k[i - 1] * (k - i + 1) % mod)*inv[i]) % mod; 25 } 26 } 27 int main() { 28 // freopen("in.txt","r",stdin); 29 init(); 30 int T; 31 ll n, m, k; 32 scanf("%d", &T); 33 for (int t = 1; t <= T; t++) { 34 scanf("%lld%lld%lld", &n, &m, &k); 35 find_Ck(k); 36 ll ans1 = 0; 37 ll sign = 1; 38 for (ll i = k; i >= 1; i--, sign = -sign) //求C(k,k)*k*(k-1)^(n-1) - C(k,k-1)*(k-1)*(k-2)^(n-1) ...... 39 ans1 = (ans1 + ((C_k[i] * i%mod)*exp_mod(i - 1, n - 1) % mod)*sign + mod) % mod; 40 ll ans2 = 1; 41 for (ll i = 1; i <= k; i++) { //C(m,k) 42 ans2 = (ans2*(m - i + 1) % mod)*inv[i] % mod; //注意除法取余运算要用逆元 43 } 44 printf("Case #%d: %lld ", t, ans1*ans2%mod); 45 } 46 return 0; 47 }