题目大意:输入是三个数字。一个长度为good array有以下限制:数组中的每个元素的范围都为;正好有满足。输出是满足条件的good array的数量。因为数量可能很多,所以对结果取模1e9+7

很容易想到dp,将状态定义为当前arr的长度为,恰好有满足。那么装转方程应为。看下数据范围,时间复杂度的话是必TLE的。

于是想到了组合数学的解法,首先是第个元素可以有种选择;然后在剩下个元素中选取个位置;也就是说这k个位置外加第个位置不用再考虑赋值了,剩下的个位置不能跟前一个相同。算式可以得到

按照公式交了一发WA,但是实际上已经过了很多样例了,感觉和取模有关。查了一下取模只有乘法分配律没有除法分配律。因为在算的时候有用到除法,所以出错了。这种情况需要用到扩展欧几里得或者费马小定理来求除数在mod下的乘法逆元,然后用乘法逆元代替除法。看了一下利用二项式展开证明费马小定理的原理。然后实现的时候用快速幂降了一下复杂度到,不然用朴素的暴力1e9+7次循环八成还是会TLE的。

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
class Solution {
public:
int mod = 1e9 + 7;

long long modPow(long long base, long long exp, int mod) {
long long result = 1;
while (exp > 0) {
if (exp % 2) result = result * base % mod;
base = base * base % mod;
exp /= 2;
}
return result;
}

long long modInverse(long long a, int mod) {
return modPow(a, mod - 2, mod);
}

int countGoodArrays(int n, int m, int k) {
long long ans = m, res = 1;

for (int i = n - 1; i >= n - k; i--) {
ans = ans * i % mod;
}

for (int i = 1; i <= k; i++) {
res = res * i % mod;
}

ans = ans * modInverse(res, mod) % mod;

for (int i = 0; i < (n - 1 - k); i++) {
ans = ans * (m - 1) % mod;
}

return (int)ans;
}
};

这题公式推出来之后,写法上就挺套路的。需要快速幂和乘法逆元的知识。