洛谷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;
}
原文地址:https://www.cnblogs.com/czhui666/p/13838149.html