题意
有排成一行的n个格子,你需要在 m 种颜色中选取 k 种颜色对这 n 个格子染色,要求k 种颜色都要使用,且相邻格子的颜色不同,求方案数(答案对 109 + 7取模)。
思路
思维性较强,需要对容斥原理有比较深的理解。
//线性求逆元公式
//inv[i] = i (% mod) inv[i] 为 i 的逆元
inv[0] = inv[1] = 1;
for (int i = 2; i <= 2e6; i++)//注意必须从2开始,i =1 时,下面公式为0
inv[i] = (mod - mod / i) * inv[mod % i] % mod;
容斥原理分析过程:
- 求出所有的方案数,也就是下面多项式中的第一项
- 求出非法方案总和
最少 1 种颜色不被使用的方案数 - 最少 2 种颜色不被使用的方案数 + 最少三种颜色不被使用的方案数 - … (显而易见,这就是容斥原理,套公式即可)
(之所以按照使用方案数递减的顺序来,是因为总方案数是最少使用k种颜色) - 合法方案 = 所有方案 - 非法方案
也就是最下面ans中的结论了 - 如果实在看不懂,建议复习一下容斥原理,然后学习一下 多重集的组合数(AcWing214),学习如何分析使用容斥原理
(图片中有几处小错误)
① inv 代表 k!的逆元
②中ans应该去掉最后一项,因为要求相邻两个格子颜色不同,所以,最少需要两种颜色
参考题解
时间复杂度 O ( k l o g n ) O(klogn) O(klogn)
Code
const int N = 1e6 + 6;
ll ksm(ll a, ll b)
{
ll ans = 1;
for (; b; b >>= 1)
{
if (b & 1) ans = ans * a % mod;
a = a * a % mod;
}
return ans;
}
ll fac[N], inv[N];//阶乘,阶乘的逆元
ll C(ll n, ll m)
{
return fac[n] * inv[m] % mod * inv[n - m] % mod;
}
int main()
{
fac[0] = inv[0] = fac[1] = inv[1] = 1;
for (ll i = 2; i < N; i++)
{
fac[i] = fac[i - 1] * i % mod;
inv[i] = (mod - mod / i) * inv[mod % i] % mod;
}
for (ll i = 1; i < N; i++)
inv[i] = inv[i] * inv[i - 1] % mod;
IOS;
int T, cas = 0; cin >> T;
while (T--)
{
ll n, m, k; cin >> n >> m >> k;
ll fackm = 1;
for (ll i = 1; i <= k; i++)
fackm = fackm * (m - i + 1) % mod;//A(k, m)
ll ans = k * ksm(k - 1, n - 1) % mod;
for (ll i = k - 1, j = 2; i >= 2; i--, j++)
{
if (j & 1) ans = (ans + C(k, i) * i % mod * ksm(i - 1, n - 1) % mod) % mod;
else ans = (ans - C(k, i) * i % mod * ksm(i - 1, n - 1) % mod + mod) % mod;
}
ans = ans * fackm % mod * inv[k] % mod;
cout << "Case #" << ++cas << ": " << ans << endl;
}
return 0;
}