luoguP4457 [BJOI2018]治疗之雨 概率期望 + 高斯消元

 

应该是最后一道紫色的概率了....然而颜色啥也代表不了....

首先看懂题意:

你现在有$p$点体力,你的体力上限为$n$

在一轮中,

1.如果你的体力没有满,你有$\frac{1}{m + 1}$的几率回复一点体力

2.紧接着有$k$轮攻击,每轮攻击都有$\frac{1}{m + 1}$的几率使你掉一点体力

如果一轮后,你的体力$ \leq 0$,那么游戏结束

询问游戏结束的期望轮数

 

看懂题应该就懂了什么吧....

设状态$f[i]$表示生命值为$i$游戏结束的期望轮数

那么

$$f[i] = \begin{cases}
& 0\;\;(i = 0)\\
& 1 + \sum\limits_{j = 1}^i f[j] * P[i - j]\;\;(i = n)\\
& 1 + \sum\limits_{j = 1}^i f[j] * (\frac{P[i - j + 1]}{m + 1} + \frac{m * P[i - j]}{m + 1}) + f[i + 1]*\frac{P[0]}{m + 1} \;\;(else)
\end{cases}$$

其中,$P[i]$表示在一轮攻击中受到$i$点攻击的概率

($f[0]$结束游戏,因此不能向别的状态转移)

 

考虑怎么求$P[i]$

由于确定了$i$种要攻击,其余的不攻击,那么一种攻击方式的概率为$(\frac{1}{m + 1})^i * (\frac{m}{m + 1})^{k - i}$

对于攻击$i$次而言,总共有$C(k, i)$种攻击方式

因此$P[i] = C(k, i) *(\frac{1}{m + 1})^i * (\frac{m}{m + 1})^{k - i} $

直接$O(n^2)$暴力全部求出来...

 

那么,有了$P[i]$后,高斯消元的复杂度过高

但是,注意到方程中$0$的数量非常的多,因此在消元的时候把$0$项跳过

酱紫,复杂度就到$O($玄学$)$了,仔细优化一下就$O(n^2)$了,具体看代码吧....

 

注:记得判无解

注2:不知道为什么$P[]$数组莫名的要多预处理一些.....

注(注2):仿佛是$n = 0$的时候挂了.....出题人有猫病啊.....

复杂度$O(Tn^2)$,没怎么卡常

#include <cstdio>
using namespace std;

extern inline char gc() {
    static char RR[23456], *S = RR + 23333, *T = RR + 23333;
    if(S == T) fread(RR, 1, 23333, stdin), S = RR;
    return *S ++;
}
inline int read() {
    int p = 0, w = 1; char c = gc();
    while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); }
    while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc();
    return p * w;
}

#define sid 1600
#define eid 200050
#define ri register int
const int mod = 1000000007;

int n, p, m, k;
int inv[eid], P[sid], f[sid][sid];

void Init_Inv() {
    inv[0] = inv[1] = 1;
    for(ri i = 2; i <= 200000; i ++)
    inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod;
    for(ri i = 2; i <= 200000; i ++)
    inv[i] = 1ll * inv[i - 1] * inv[i] % mod;
}

int fp(int a, int k) {
    int ret = 1;
    for( ; k; k >>= 1, a = 1ll * a * a % mod)
    if(k & 1) ret = 1ll * ret * a % mod;
    return ret;
}

void Init_P() {
    int invm = fp(fp(m + 1, mod - 2), k);
    for(ri i = 0; i <= n + 5; i ++) {
        if(i > k) { P[i] = 0; continue; }
        int C = 1;
        for(ri j = k - i + 1; j <= k; j ++) C = 1ll * C * j % mod;
        C = 1ll * C * inv[i] % mod;
        P[i] = 1ll * C * fp(m, k - i) % mod * invm % mod;
    }
}

void Init_Guass() {
    int m11 = fp(m + 1, mod - 2);
    int mm1 = 1ll * m * m11 % mod;
    for(ri i = 1; i <= n + 1; i ++)
    for(ri j = 1; j <= n + 1; j ++)
    f[i][j] = 0;
    for(ri i = 1; i < n; i ++) {
        f[i][i] = 1; f[i][n + 1] = 1;
        f[i][i + 1] = (mod - 1ll * P[0] * m11 % mod);
        for(ri j = 1; j <= i; j ++) {
            f[i][j] = (f[i][j] - 1ll * m11 * P[i - j + 1] % mod + mod) % mod;
            f[i][j] = (f[i][j] - 1ll * mm1 * P[i - j] % mod + mod) % mod;
        }
    }
    f[n][n] = 1; f[n][n + 1] = 1;
    for(ri i = 1; i <= n; i ++) f[n][i] = (f[n][i] - P[n - i] + mod) % mod;
}

void Guass() {
    for(ri i = 1; i <= n; i ++) {
        int inv = fp(f[i][i], mod - 2);
        for(ri j = i + 1; j <= n; j ++) {
            int t = 1ll * f[j][i] * inv % mod;
            for(ri k = i; k <= i + 1; k ++) 
            f[j][k] = (f[j][k] - 1ll * f[i][k] * t % mod + mod) % mod;
            f[j][n + 1] = (f[j][n + 1] - 1ll * f[i][n + 1] * t % mod + mod) % mod;
        }
    }
    for(ri i = n; i >= 1; i --) {
        f[i][n + 1] = 1ll * f[i][n + 1] * fp(f[i][i], mod - 2) % mod;
        f[i - 1][n + 1] = (f[i - 1][n + 1] - 1ll * f[i - 1][i] * f[i][n + 1] % mod + mod) % mod; 
    }
}

int main() {
    int Tt = read();
    Init_Inv();
    while(Tt --) {
        n = read(); p = read(); m = read(); k = read();
        if(k == 0) { printf("-1\n"); continue; }
        if(m == 0 && k == 1) { printf("-1\n"); continue; }
        Init_P(); Init_Guass(); Guass();
     int ans = f[p][n + 1];
        printf("%d\n", ans);
    }
    return 0;
}

 

posted @ 2018-08-22 13:49  remoon  阅读(206)  评论(0编辑  收藏  举报