0%

NC13884.Paint Box

NC13884.Paint Box

题目描述:

$ n $ 个盒子,使用 $ m $ 种颜色染色,相邻的盒子不能染相同的颜色。

问恰好使用了 $ k $ 种颜色的染色方案数是多少?

数据范围:
$ 1\le n,m\le 10^9, 1\le k\le 10^6,k\le n,m $

题解:

恰好使用了 $ k $ 种颜色可以使用容斥进行转换。考虑至多或至少。

假设至多使用 $ k $ 种颜色染色,有 $ k(k - 1)^n $ 种方案数。则 $ f_k = k (k - 1)^n = \sum_{i=0}^k C_k^i g_k $ 。

根据容斥原理 $ g_k = f_k - C_k^1 f_{k-1} + C_k^2 f_{k-2} + \cdots + (-1)^kf_0 $

其实也就是容斥原理的公式,减去一部分需要加上重复减掉的,注意系数。

得到结果后需要乘以 $ C_m^k $ ,因为可以任选 $ k $ 个颜色。

实际写代码时, $ f_k $ 可以通过预处理得到, $ C_k^i $ 也可以预处理,也可以直接迭代, $ C_k^i = C_k^{i - 1} * (k - i + 1) / i $ ,使用循环直接求的话需要注意初始化为 $ inv[k + 1] $

但是 $ C_m^k $ 由于 $ m $ 比较大, $ k $ 比较小,可以通过定义求解。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
using namespace std;
using namespace FAST_IO;
const ll mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const ll INF_LL = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-5;
const int maxn = 1e6 + 10;
const int maxm = 1e5 + 10;
int t;
ll n, m, k;
ll inv[maxn];
void init(int n)
{
inv[0] = inv[1] = 1;
for (int i = 2; i <= n; i++)
{
inv[i] = (mod - mod / i) * inv[mod % i] % mod; // -k 写作了 p - k  
}
}
ll Cnm(ll n, ll m)
{
if (n < m)
return 0;
if (n - m < m)
m = n - m;
ll a = 1;
for (int i = 0; i < m; ++i)
{
a = a * (n - i) % mod * inv[i + 1] % mod;
}
return a;
}

ll qpow(ll a, ll b)
{
ll ans = 1, tmp = a;
while (b)
{
if (b & 1)
ans = ans * tmp % mod;
tmp = tmp * tmp % mod;
b >>= 1;
}
return ans;
}
int main()
{
// #define COMP_DATA
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
int tcase;
read(tcase);
init(1e6 + 1);
while (tcase--)
{
read(n, m, k);
ll ans = 0;
ll cki = inv[k + 1];
for (int i = 0, flag = 1; i < k; ++i, flag = -flag)
{
cki = cki * (k - i + 1) % mod * inv[i] % mod;
ans = (ans + flag * (k - i) * qpow(k - i - 1, n - 1) % mod * cki + mod) % mod;
}
ans = ans * Cnm(m, k) % mod;
cout << ans << endl;
}
return 0;
}