题目链接:http://codeforces.com/gym/100548/attachments
有n个物品 m种颜色,要求你只用k种颜色,且相邻物品的颜色不能相同,问你有多少种方案。
从m种颜色选k种颜色有C(m, k)种方案,对于k种颜色方案为k*(k-1)^(n-1)种。但是C(m, k)*k*(k-1)^(n-1)方案包括了选k-1,k-2...,2种方案。
题目要求刚好k种颜色,所以这里想到用容斥。
但是要是直接C(m, k)*k*(k-1)^(n-1) - C(m, k-1)*(k-1)*(k-2)^(n-1)的话,其实是多减的。
比如k=4,4种颜色1 2 3 4,有种染色方案是1 2 1 2,那么k-1的话1 2 3或者1 2 4也有染色方案是1 2 1 2,这里发现多减了。所以k-2还得加上。
所以公式就变成了
C(m, k)*( k*(k-1)^(n-1) - C(k, k-1)*(k-1)*(k-2)^(n-1) + C(k, k-2)*(k-2)*(k-3)^(n-1) ... )
中间的组合数C(k, i),可以用逆元来解决。C(m, k)则暴力即可。
1 //#pragma comment(linker, "/STACK:102400000, 102400000") 2 #include <algorithm> 3 #include <iostream> 4 #include <cstdlib> 5 #include <cstring> 6 #include <cstdio> 7 #include <vector> 8 #include <cmath> 9 #include <ctime> 10 #include <list> 11 #include <set> 12 #include <map> 13 using namespace std; 14 typedef __int64 LL; 15 typedef pair <int, int> P; 16 const int N = 1e6+ 5; 17 LL mod = 1e9 + 7; 18 LL f[N]; 19 20 LL Pow(LL a , LL n , LL mod) { 21 LL res = 1; 22 while(n) { 23 if(n & 1) 24 res = res * a % mod; 25 a = a * a % mod; 26 n >>= 1; 27 } 28 return res; 29 } 30 31 LL Cnm(LL n, LL m, LL mod) { 32 if(n < N) { //数据范围小的 逆元就好了 33 return f[n]*Pow(f[m]*f[n-m]%mod, mod - 2, mod) % mod; 34 } 35 LL res = Pow(f[m]%mod, mod - 2, mod)%mod; 36 for(LL i = 0; i < m; ++i) { 37 res = res * (n-i) % mod; 38 } 39 return res%mod; 40 } 41 42 void init() { //阶乘预处理 43 f[0] = 1; 44 for(LL i = 1; i < N; ++i) 45 f[i] = f[i - 1] * i % mod; 46 } 47 48 int main() 49 { 50 init(); 51 int t; 52 LL n, m, k; 53 scanf("%d", &t); 54 for(int ca = 1; ca <= t; ++ca) { 55 scanf("%lld %lld %lld", &n, &m, &k); 56 printf("Case #%d: ", ca); 57 if(k == 1 && n == 1) { 58 printf("%lld\n", m); 59 continue; 60 } 61 else if(k == 1) { 62 printf("0\n"); 63 continue; 64 } 65 LL ans = 0, temp = k; 66 for(int i = 1; k >= 2; --k, i = -i) { 67 ans = (i * Cnm(temp, k, mod) % mod *k % mod* Pow(k- 1, n - 1, mod) % mod + ans) % mod; 68 } 69 ans = ans * Cnm(m, temp, mod) % mod; 70 printf("%lld\n", (ans + mod) % mod); 71 } 72 return 0; 73 }
之前纠结了好久...
时间: 2024-10-13 04:04:12