洛谷10月月赛T2 深海少女与胖头鱼 期望DP
深海少女与胖头鱼
题目链接
期望DP。考场上没想出来,考完机房里一个大佬给我讲的%%%。
\(f[n][m]\)表示击败\(n\)个带圣盾的和\(m\)个不带圣盾的所需要的期望攻击次数,可以列出DP转移方程:\(f[n][m] = \frac{n}{n + m} f[n + m - 1][1] + \frac{m}{n + m} f[n][m - 1] + 1\)。
应该还挺好理解的:如果本次攻击了带圣盾的,那么这只带圣盾的会变成一只普通的,其他所有的都会带上圣盾;如果攻击了没有带圣盾的,那么将会减少一只胖头鱼。
然后我们带一下特殊解,当\(m = 0\)时,\(f[n][0] = f[n - 1][1] + 1\)。
当\(m = 1\)时,\(f[n][1] = \frac{n}{n+ 1}f[n][1] + \frac{1}{n + 1} f[n][0] + 1\),然后两边同乘一个\(n +1\),再移项可以得到:
\(f[n][1] = f[n][0] + n + 1 = f[n - 1][1] + n + 2 = \frac{n ^ 2 + 5n + 2}{2}\),那么\(f[n][0]\)也可以带入得到:\(f[n][0] = \frac{n ^ 2 + 3n}{2}\)。
然后我们看最上面那个DP转移方程,\(n\)那一维可以省去,前半部分可以\(O(1)\)求,后半部分递推求就好了。复杂度\(O(m \ logn)\)
#include <bits/stdc++.h>
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 1e6 + 5, mod = 998244353;
int f, n, m, inv2, inv[N];
int ksm(int x, int y, int mod) {
int res = 1;
while(y) {
if(y & 1) res = 1ll * res * x % mod;
x = 1ll * x * x % mod; y >>= 1;
}
return res;
}
int calc_frac(int x, int y) {
return 1ll * x * y % mod;
}
int calc(int x) {
return (1ll * x * x % mod + 1ll * 5 * x % mod + 2) % mod * inv2 % mod;
}
void make_pre(int x) {
for(int i = 1;i <= x; i++) inv[i] = ksm((n + i) % mod, mod - 2, mod);
}
int main() {
n = read() % mod; m = read(); inv2 = ksm(2, mod - 2, mod);
make_pre(m);
f = (1ll * n * n % mod + 1ll * 3 * n % mod) % mod * inv2 % mod;
for(int i = 1;i <= m; i++)
f = (1ll * calc((n + i) % mod - 1) * calc_frac(n, inv[i]) % mod + 1ll * calc_frac(i, inv[i]) * f % mod + 1) % mod;
printf("%d", f);
return 0;
}