[HNOI2015]亚瑟王

传送门

题解

  是一道与概率期望有关的一道题。

  博主首先考虑(n)张卡牌中选出来(r)张,此时贡献很显然,可是对于这(r)张牌,不同的顺序对应的概率不同,这样做期望很难统计。

  换个角度,我们来单独看每张卡牌,分别算出每张卡牌被选出来的概率,进而得到期望,最后由于期望的线性性加起来也能得到答案。我们先看所有卡牌中选第(i)张的概率,记作(g_i),答案即为(sumlimits_{i=1}^nd_ig_i),发现这样的状态不够,就再设(f_{i,j})表示前(i)张中出了(j)张的概率,那么对于第(i)张牌,前面(i-1)轮出了(j)张,会有(r-j)轮经过第(i)张卡牌,如果第(i)张出,此时概率为(f_{i-1,j-1} imes left(1-(1-p_i)^{r-j} ight)),实际上就是只要这张牌出了的概率;如果第(i)张没出,概率为(f_{i-1,j} imes (1-p_i)^{r-j}),表示一次都没出。综上我们可以得到:

[f_{i,j}=f_{i-1,j} imes(1-p_i)^{r-j}+f_{i-1,j-1} imesleft(1-(1-p_i)^{r-j} ight) ]

[g_i=sum_{j=0}^{min(i,r)-1}f_{i-1,j} imesleft(1-(1-p_i)^{r-j} ight) ]

  边界判掉即可。复杂度:( ext{O}(Tnr))

代码

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

#define ll long long
#define ull unsigned long long
#define min(a, b) (a) < (b) ? (a) : (b)
#define max(a, b) (a) > (b) ? (a) : (b)
#define rep(i, a, b) for (int i = a, i##end = b; i <= i##end; ++i)
#define per(i, a, b) for (int i = a, i##end = b; i >= i##end; --i)
#define rep0(i, a) for (int i = 0, i##end = a; i < i##end; ++i)
#define per0(i, a) for (int i = a-1; ~i; --i)
#define chkmax(a, b) a = max(a, b)
#define chkmin(a, b) a = min(a, b)

const int maxn = 255;

double f[maxn][maxn], p[maxn], g[maxn];
int d[maxn], T, n, r;

double pw[maxn][maxn]; // 预处理概率的幂

int main() {
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d", &n, &r);
        rep(i, 1, n) scanf("%lf%d", &p[i], &d[i]);
        memset(f, 0, sizeof f);
        f[0][0] = 1; // 初始化
        double ans = 0;
        rep(i, 1, n) {
            pw[i][0] = 1, g[i] = 0;
            rep(j, 1, r) pw[i][j] = pw[i][j-1] * (1-p[i]); // 预处理
            rep(j, 0, min(i, r)) f[i][j] = (j ? f[i-1][j-1] * (1-pw[i][r-j+1]): 0) + f[i-1][j] * pw[i][r-j]; // 计算f[i][j]
            rep(j, 0, min(i, r)-1) g[i] += f[i-1][j] * (1-pw[i][r-j]); // 计算g[i]
            ans += g[i] * d[i]; // 由期望的线性性相加
        }
        printf("%.10lf
", ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/ac-evil/p/12237257.html