-
前言
这是本人第一次做期望 DP 题, 难免有误, 但会尽力讲清楚.
-
题目分析
这是一道期望 DP 题.
观察数据范围, (n leq 10^{14}), 可以大胆猜测正解时空复杂度应该与 (n) 无关.
但首先推出朴素 DP 方程:
设F[n][m]表示有 n 只拥有圣盾的胖头鱼和 m 只没有圣盾的胖头鱼时的期望造成伤害次数
F[n][m] = n / (n + m) * (F[n + m - 1][1] + 1) + m / (n + m) * (F[n][m - 1] + 1);
上式应该不难得知.
但 n 的范围过大, 这个方法是过不了的.
观察上式中, 问题在于这一部分:
... = n / (n + m) * (F[n + m - 1][1] + 1) + ...
这直接导致连暴力都难写出来...因为转移顺序不会了(我太菜了)
比赛时的我发现事情有点不对劲, 而又不甘止步于此.
于是又瞄准了另一特殊部分分: (m = 0)
也就是:
F[n][0] = ?
手推算出: F[n][0] = (n + 1) * n / 2
然后我又发现:
F[n][1] = F[n][0] + n + 1 = n * (n + 3) / 2 + 1
再转过头来看朴素的 DP 方程, 发现可以直接算出 F[n + m - 1][1]
令 F[n + m - 1][1] = S
此时 F[n][m] = n / (n + m) * (S + 1) + m / (n + m) * (F[n][m - 1] + 1)
发现 DP 方程第一维可以直接消去. 然后, 就做到了与 (n) 无关的转移.
而我求逆元用的是快速幂法, 边转移边求, 时间复杂度还带一个 (log)
-
代码
#include <cstdio>
typedef long long LL;
const int N = 1e6;
const LL Mod = 998244353;
LL n, m, F[N + 5];
LL Qpow(LL, LL);
LL Fizz(LL LL);
int main() {
scanf("%lld%lld", &n, &m), n %= Mod;
F[0] = Fizz(n);
LL Inv2 = Qpow(2, Mod - 2);
for(LL i = 1; i <= m; i ++) {
F[i] = n % Mod * (n + i + 3) % Mod * Inv2 % Mod;
F[i] = (F[i] + i * Qpow(n + i, Mod - 2) % Mod * (F[i - 1] + 1) % Mod) % Mod;
} printf("%lld
", F[m]);
return 0;
} LL Fizz(LL p) {
p = p % Mod;
return ((p + 1) * p / 2 % Mod + p) % Mod;
} LL Qpow(LL a, LL b) {
LL ans = 1;
a = (a % Mod + Mod) % Mod;
for (; b; b >>= 1) {
if (b & 1) ans = (a * ans) % Mod;
a = (a * a) % Mod;
} return ans;
}