LuoguP6857 【深海少女与胖头鱼】

  • 前言

这是本人第一次做期望 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;
}
原文地址:https://www.cnblogs.com/Rothen/p/13846007.html