Codeforces 100548F - Color (组合数+容斥)

题目链接: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 }
View Code

之前纠结了好久...

转载于:https://www.cnblogs.com/Recoder/p/5773486.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值