【BZOJ】4008: [HNOI2015]亚瑟王
题解
一个小数的一百多次方也没有爆,exm???
这个如果单看题面的话,这题非常的不好搞(什么题能告诉你它好搞><
我们放弃对每一局求贡献,转而求一个卡牌被使用的概率
怎么求,我们发现,只考虑前i - 1个被考虑后,有多少个局剩下了我们能用
这么想,我们从第一个数开始考虑,显然,它每次有R个局能用,它能被使用的概率就是\(1 - (1 - P_{1})^{R}\)
对于第二个数,他有概率是有R局都考虑它,还有概率是R - 1局考虑它,具体的概率还需要dp了
dp的方程就是
\(dp[i][j]\)表示考虑了前i个数,到了第i个数的时候,有j局还没有用
\(dp[i][j] = (1 - P_{i - 1})^{j}dp[i - 1][j] + (1 - (1 - P_{i - 1})^{j + 1})dp[i - 1][j + 1]\)只要考虑第i - 1个数有没有被使用就好
求期望就对每张牌统计一下概率就好
代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <map>
//#define ivorysi
#define pb push_back
#define space putchar(' ')
#define enter putchar('\n')
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define mo 974711
#define RG register
#define MAXN 200005
using namespace std;
typedef long long int64;
typedef double db;
int T,N,R;
db f[305][150],powP[305][150],ans,P[350],D[350],s[350];
void Process() {
ans = 0.0;
scanf("%d%d",&N,&R);
for(int i = 1 ; i <= N ; ++i) {
scanf("%lf%lf",&P[i],&D[i]);
}
for(int i = 0 ; i <= N ; ++i) {
powP[i][0] = 1.0;
for(int j = 1 ; j <= R ; ++j) {
powP[i][j] = powP[i][j - 1] * (1.0 - P[i]);
}
}
memset(f,0,sizeof(f));
memset(s,0,sizeof(s));
f[0][R] = 1;
for(int i = 1 ; i <= N ; ++i) {
for(int j = 1 ; j <= R ; ++j) {
f[i][j] = f[i - 1][j] * powP[i - 1][j] + f[i - 1][j + 1] * (1.0 - powP[i - 1][j + 1]);
s[i] += f[i][j] * (1.0 - powP[i][j]);
}
}
for(int i = 1 ; i <= N ; ++i) ans += s[i] * D[i];
}
void Solve() {
scanf("%d",&T);
while(T--) {
Process();
printf("%.10lf\n",ans);
}
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
Solve();
return 0;
}