若用ai表示造成i点伤害的事件个数,则对应的生成函数[1]: G(x) = (x ^ 3 + x ^ 4 + x ^ 5 + x ^ 6) ^ n = x ^ (3 * n) * (1 + x + x ^ 2 + x ^ 3) = x ^ (3 * n) * (1 + x) ^ n * (1 + x ^ 2) ^ n 因式分解这步对这个解法来说非常重要,感觉不写出生成函数是看不出的。 x ^ (3 * n)可以忽略,直接令 m 减去 2 * n 即可。 接下来考虑如何计算(1 + x) ^ n的展开式的各项系数。 由C(n, k) = C(n - 1, k - 1) * (n - k + 1) / k,我们可以比较快地计算出各项系数。然而这其中除法运算是个问题,因为模运算和除法运算之间是没有交换律的。在模意义下,我们可以用乘法逆元[2]来解决这个问题。 求出(1 + x) ^ n和(1 + x ^ 2) ^ n的展开式的各项系数之后,我们可以在O(n)时间内求出答案。具体做法是对其中一个系数序列求后缀和,然后累加另一序列的系数和相应后缀和的乘积即可。 以系数序列a[]={1, 2, 3}和b[]={2, 5, 7}为例。b[]的后缀和为c[]={14, 12, 7},如果我们要求二次以上项的系数和,那么只需求a[0] * c[2] + a[1] * c[1] + a[2] * c[0] = a[0] * b[2] + a[1] * (b[1] + b[2]) + a[2] * (b[0] + b[1] + b[2])。求后缀和和统计的部分复杂都是O(n)。 [1] 对于一个数列{ai},对应的生成函数为G(x) = Sigma(0, n, ai * x ^ i),其中n为最高项,可能为无穷。具体可见《离散数学及其应用》第七版8.4节或者维基百科:https://zh.wikipedia.org/wiki/%E6%AF%8D%E5%87%BD%E6%95%B0 [2] 如果有a * b ≡ 1 (mod q),那我们称a和b互为乘法逆元。根据费马小定理[3],若a为整数,p为质数,a与q互质,则有a ^ (p - 1) ≡ 1 (mod p),。因而模p下a和a ^ (p - 2)互为乘法逆元。而a ^ (p - 2)可以用快速幂[4]在O(logp)时间内计算出。另外也可以用解同余方程的方法求乘法逆元。 [3] 费马小定理:https://zh.wikipedia.org/wiki/%E8%B4%B9%E9%A9%AC%E5%B0%8F%E5%AE%9A%E7%90%86 [4] 快速幂:具体可见《离散数学及其应用》第七版4.3节或者搜索引擎