洛谷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;
}
posted @ 2020-10-19 06:24  C锥  阅读(98)  评论(0编辑  收藏  举报