Gym100548F Color
题目链接: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\times[C_{k}^{k}G(k) - C_{k}^{k-1}G(k-1) + ... +(-1)^{k-2}G(2)]\)
\(=C_m^k\times[C_{k}^{k}k(k-1)^{n-1} - C_{k}^{k-1}(k-1)(k-2)^{n-1} + ... +(-1)^{k-2}\times2]\).
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\n", t, ans1*ans2%mod); 45 } 46 return 0; 47 }
“这些年我一直提醒自己一件事情,千万不要自己感动自己。大部分人看似的努力,不过是愚蠢导致的。什么熬夜看书到天亮,连续几天只睡几小时,多久没放假了,如果这些东西也值得夸耀,那么富士康流水线上任何一个人都比你努力多了。人难免天生有自怜的情绪,唯有时刻保持清醒,才能看清真正的价值在哪里。”