组合计数的重要性,居然忘了求组合数用卢卡斯加速!
得找到公式,进行容斥
题解:
´
容斥原理
´
首先,用
k
种颜色的方案为
c(
k,k
)*(k)*(k-1)^(n-1)
´
从
k
种颜色方案中减去用
k-1
种颜色方案
c(k,k-1)*(k-1)*(k-2)^(n-1),
得到恰好用
k
种颜色方案数。
´
多减去的
k-2
种颜色方案数
c(k,k-2)*(k-2)*(k-3)^(n-1)
要重新加上,依此类推
´
C(
m,k
)*Sigma(c(
k,k-i
)*(k-
i
)*(k-i-1)^(n-1)*(-1)^(
i
))(0<=
i
<k)
加速
加速
´
a(k)
表示用不超过
k
种颜色染
n
个位置,两两相邻颜色不相同的总数,很简单
a(k)=k(n-1)^(k-1)
´
b(k)
表示恰好用
k
种颜色
´
很显然
a(k)=ΣC(
k,i
)b(
i
)
,我们知道
a
,想知道
b
,这里就用到二项式反演
´
´
´
那么
b(k)=
Σ
C(
k,i
)*
i
*(-1)^(k-
i
)*(n-1)^(k-1)
´
再利用
kC
(
n,k
)=
nC
(n-1,k-1)
代入,就可以写成二项式展开的式子,从而用快速幂加速
´
然后就是枚举
k
,
ans
=
Σ
C(
m,k
)*b(k)
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <assert.h>
#define IOS ios::sync_with_stdio(false)
#include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <bitset>
using namespace std;
typedef long long int LL;
//快速幂
LL quick_pow(LL a, LL b, LL MOD) {
a = a % MOD;
LL ans = 1;
while (b > 0) {
if (b & 1) {
ans = ans * a % MOD;
}
a = a * a % MOD;
b >>= 1;
}
return ans;
}
LL fac[1000000 + 20];//预处理阶乘
//卢卡斯定理(全预处理型)
LL C2(LL n, LL m, LL MOD) {
if (n < m) return 0; //防止sb地在循环,在lucas的时候
if (n == m) return 1 % MOD;
LL ans1 = 1;
LL ans2 = 1;
LL mx = max(n - m, m); //这个也是必要的。能约就约最大的那个
LL mi = n - mx;
for (int i = 1; i <= mi; ++i) {
ans1 = ans1 * (mx + i) %MOD;
ans2 = ans2 * i % MOD;
}
return (ans1 * quick_pow(ans2, MOD - 2, MOD) % MOD); //这里放到最后进行,不然会很慢
}
LL C(LL n, LL m, LL MOD) {
if (n > 1000000) return C2(n, m, MOD);
return fac[n] * quick_pow(fac[n - m], MOD - 2, MOD) % MOD * quick_pow(fac[m], MOD - 2, MOD) % MOD;
}
////卢卡斯定理(全预处理型)
const int MOD = 1e9 + 7;
void add(LL &x, LL y) {
x += y;
x += MOD;
if (x >= MOD) x %= MOD;
}
void work()
{
int n, m, k;
scanf("%d%d%d", &n, &m, &k);
if (n == 1) {
printf("%d\n", m);
return;
}
if (k == 1) {
printf("0\n");
return;
}
LL ans = 0;
LL res = C(m, k, MOD);
k--;
n--;
for (int i = 0; i <= k; ++i) {
if (i & 1) {
add(ans, -(C(k, i, MOD) * quick_pow(k - i, n, MOD)) % MOD);
} else {
add(ans, (C(k, i, MOD) * quick_pow(k - i, n, MOD) % MOD));
}
}
ans = ans * quick_pow(fac[k], MOD - 2, MOD) % MOD;//费马小定理求逆元
ans = ans * fac[k + 1] % MOD;//结果要乘以K的阶乘
ans = ans * res % MOD;
printf("%lld\n", ans);
}
int main() {
fac[0] = 1;
for (int i = 1; i <= 1000000; ++i)
{
fac[i] = fac[i - 1] * i % MOD;
}
int t;
scanf("%d", &t);
while (t--) work();
return 0;
}