MOD = 1000000007 # 扩展欧几里得算法 def extended_gcd(a, b): if a == 0: return (b, 0, 1) else: g, y, x = extended_gcd(b % a, a) return (g, x - (b // a) * y, y) def mod_inverse(p, q): g, x, _ = extended_gcd(p, q) if g != 1: raise ValueError(f"{p} 和 {q} 不互质,无法找到模逆元") else: # 确保结果是正数 inverse = x % q return inverse n = int(input()) s = 0 m = 0 T = input().split() for t in T: tmp = int(t) bt = tmp % 2 s += tmp m += bt a = [[0, 0] for _ in range(n)] a[n - 1] = [1, 1] for i in range(n - 1, 0, -1): a[i - 1][1] = a[i][1] * i a[i - 1][0] = (a[i][0] + a[i][1]) * (n - i) + a[i - 1][1] a[i - 1][0] %= MOD a[i - 1][1] %= MOD g, _, _ = extended_gcd(a[i - 1][0], a[i - 1][1]) a[i - 1][0] //= g a[i - 1][1] //= g b = [0] * (n + 1) for i in range(m): b[i] = a[i][0] * mod_inverse(a[i][1], MOD) % MOD s += b[i] s %= MOD print(s)
问题拆解
每次操作,总点赞数+1,因此,最终点赞数=最初点赞数+随机事件发生总次数
可以明显看到,各个笔记点赞数之间地位无差别,且具体值并不重要,重要的只有点赞数之和以及当前状态(剩余多少个奇数)。
因此,问题的核心就很明显了:一共 n 个数,其中有 m 个奇数,每一步会随机地有一个数改变自己的奇偶性,求解当所有数都成为偶数时的期望步数(求出步数再加上初始点赞和s0)
显然,这是一个动态规划问题。
动态规划
让我们将剩余i个奇数的状态记作状态 i ,状态 i 走到 i - 1 的步数X(i-1)的期望记作 x(i-1) = q(i-1)/p(i-1) 。
由于目标是全部偶数,也就是状态0,这里将奇数减少称为前进,增多称为后退。
在动态规划问题中,推算某个变量X的期望x时,引用了另一期望为y变量Y,可以将Y当成固定为y的常量以简化推理。
从 n(全奇数)到 n - 1:
步数 X(n-1) = 1 (q(n-1)=p(n-1)=1)
从 i 到 i - 1:
(0) 后退0次,概率 i/n ,1步
(1) 后退1次,概率 ((n-i)/n)*(i/n),1+1+Xi 步
……
(k) 后退k次,概率 ((n-i)/n)^k*(i/n),1+(1+Xi)*k 步
……
求和,得到
x(i-1) = i/n * 1 + ((n-i)/n)*(i/n) * (1+1+Xi) + …… + ((n-i)/n)^k*(i/n) * (1+(1+Xi)*k) + ……
用Xi的期望xi替换Xi,计算得到:
x(i-1) = 1 + (1+xi) * ((n-i)/i)
也就是
p(i-1)_tmp = pi * i
q(i-1)_tmp = (qi + pi) * (n-i) + p(i-1)_tmp
p(i-1)_tmp q(i-1)_tmp约分后得到p(i-1)、q(i-1)
从 m 到 0
依据以上递推关系和初值,一直从x(n-1)推算到x0,将它们存放到一个数组中。
题目所要求的从m到0,即计算 total = x0+x1+…+x(m-1) mod 1000000007
实际操作时,对于xi,先计算 xi mod 1000000007,即 qi/pi mod 1000000007 再进行求和可解。
最后,输出 total+s0。