题目大意:有n个盒子,总共有m中可能颜色,问有多少种染色方式,使得相邻的盒子不相邻,且恰好有k中颜色。数据范围n和m都非常大,k不超过1E6。
https://ac.nowcoder.com/acm/problem/13884
如果n和m的范围不大,这题可以用一个DP来完成,即考虑前i个盒子恰好k种颜色的染发。那么(i,k)来自(i-1,k)并且选择这k种颜色中不是i-1的颜色的一种,有k-1种选法;或来自(i-1,k-1)并且选择剩下的m-(k-1)种颜色中的一种,有m-k+1种选法。时间复杂度为O(n*k)。
但是这道题显然要求更快,那么首先想到可以不妨假设选到的k种颜色就是1,2,...,k。那么答案就是这个数字乘以choose(m,k)。接下来就是如何计算恰好选满1,2,...,k的选法。
我们首先考虑只能从1,2,...,s种选择的染色方案数,乘法原理,第一个数字有s种选法,剩下的每次都有(s-1)种选法,即。那么能否利用这一结论计算总数呢?只要使用容斥原理即可:首先算出只能从1,2,...,k的方案数,减去1,2,...,(k-1)的方案数,1,2,...,(k-1),k的方案数,等等;加上所有k-2个的方案数;减去所有k-3的方案数;...。其中s个的方案数就是
。
需要注意的几个点就是choose的计算,这里可以使用基于快速幂的inversion,还有就是可以直接使用快速幂。有一个容易出错的地方是用容斥原理减的时候要防止算出负数,这里可以特别当心一点,我用了一个临时变量算乘法,然后取模,不然会出现负数,总之C++要非常当心这种mod下的乘法。Debug的时候用了DP对拍很有效,找到了很多bug。
ll n, m, k;
ll power(ll x, ll k) {
ll ret = 1LL;
while(k) {
if(k & 1LL) {
ret *= x;
ret %= MOD;
}
k >>= 1;
x *= x;
x %= MOD;
}
return ret;
}
ll inv(ll x) {
return power(x,MOD-2);
}
ll choose(ll m, ll k) {
ll ans = 1;
REP(i, k) {
ans *= m - i;
ans %= MOD;
ans *= inv(i + 1);
ans %= MOD;
}
return ans;
}
ll doit() {
ll mult = choose(m, k);
// cerr << "choose" << m << ", " << k << ":" << mult << endl;
ll ans = 0;
ll sign = 1;
ll choices = 1;
for (int c = k; c >= 1; --c) {
ll pw = c;
pw *= power(c - 1, n - 1);
pw %= MOD;
// cerr << "choices: " << choices << endl;
// cerr << "pw: " << pw << endl;
ll tmp = choices * pw;
tmp %= MOD;
if (sign == 1) {
ans += tmp;
} else {
ans += MOD - tmp;
}
ans %= MOD;
sign = -sign;
choices *= c;
choices %= MOD;
choices *= inv(k - c + 1);
choices %= MOD;
}
mult *= ans;
mult %= MOD;
return mult;
}
int main(int argc, char* argv[]) {
FET(t){
read(n,m,k);
print(doit());
}
return 0;
} 
京公网安备 11010502036488号