4008: [HNOI2015]亚瑟王

时间:2023-12-11 22:10:26

4008: [HNOI2015]亚瑟王

链接

分析:

  根据期望的线性性,直接求出每张牌出现的概率,最后乘以攻击力就是答案。

  每张牌出现的概率只与它前面的牌有关,与后面的没有关系,于是按顺序考虑每张牌。

  $f[i][j]$表示到第i张牌,还剩j次出牌的机会(即轮数)的概率,那么有$f[0][r] = 1.0$,然后考虑如何转移。

  $f[i][j] = f[i - 1][j] \times (1-p[i])^j + f[i - 1][j + 1] \times (1 - (1 - p[i])^{j+1})$

  第一项表示第i张牌没有造成伤害的概率,那么后面的轮数中,选到i的时候,概率都是$1-p[i]$,共j轮,所以是$(1-p[i])^j$。

  第二项表示第i张牌造成伤害的概率,那么后面的轮中,只要有一轮抽中即可,一共j+1轮,j+1轮都没有的概率是$(1-p[i])^{j +1}$,那么只要抽中一轮即可,就是$1-(1-p[i])^{j +1}$。

  第i张牌出现的概率就可以用第二项求出。

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
#include<cctype>
#include<set>
#include<map>
#include<queue>
#include<vector>
#define fi(s) freopen(s,"r",stdin)
#define fo(s) freopen(s,"w",stdout)
using namespace std;
typedef long long LL; inline int read() {
int x=,f=;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-;
for(;isdigit(ch);ch=getchar())x=x*+ch-'';return x*f;
} const int N = ;
double f[N][N], p[N], mi[N][N];
int a[N]; void solve() {
int n = read(), r = read();
for (int i = ; i <= n; ++i) scanf("%lf%d", &p[i], &a[i]);
for (int i = ; i <= n; ++i) {
mi[i][] = 1.0;
for (int j = ; j <= r; ++j) mi[i][j] = mi[i][j - ] * ( - p[i]);
}
double ans = ;
memset(f, , sizeof(f));
f[][r] = 1.0;
for (int i = ; i <= n; ++i) {
double now = ;
for (int j = r; ~j; --j) {
f[i][j] = f[i - ][j] * mi[i][j] + f[i - ][j + ] * ( - mi[i][j + ]);
now += f[i - ][j + ] * ( - mi[i][j + ]);
}
ans += 1.0 * now * a[i];
}
printf("%.10lf\n", ans);
}
int main () {
for (int T = read(); T --; solve());
return ;
}