Problem

共有n个地雷,r句话 
1如果这个地雷在之前中已经被触发过,则
1.1如果这个地雷不是最后一个,则跳过之(考虑下一个地雷);
1.2否则(是最后一张),结束这一句话,考虑下一句。
2否则(这个地雷没有被触发),设这个地雷为第i个
2.1将其以 pi的概率触发。
2.2如果地雷被触发,则对泉岭精神造成di个单位时间的禁言,并结束这一句话。
2.3(如果没被触发)如果这个地雷已经是最后一个(即i等于n),则结束这一句话;否则,考虑下一个地雷。

Solution

用f[i][j]表示前i个地雷引爆了j个(或者说有j句话触雷)的概率。
用g[i][j]表示前i个地雷引爆了j个(或者说有j句话触雷)的期望。
首先初始化:f[0][0]=1。
1.不取对于第i+1个地雷。
f[i+1][j]+=f[i][j]*(1-p[i+1])^(r-j)。
(1-p[i+1])^(r-j)表示剩下没有触雷的r-j句话中一直不取i+1这个地雷的概率。
同理,g[i+1][j]+=g[i][j]*(1-p[i+1])^(r-j)
2.如果取这个i+1号地雷。
f[i+1][j+1]+=f[i][j]*(1-(1-p[i+1])^(r-j))
g[i+1][j+1]+=(f[i][j]*d[i+1]+g[i][j])*(1-(1-p[i+1])^(r-j))
然而突然冒出一个问题:1-(1-p[i+1])^(r-j)难道不是这个地雷在剩下的话中至少炸一次的概率吗?
我们可以换一种推法:后面的第1句话取到了地雷,那么概率为p[i+1]
后面的第2句话取到了地雷,那么概率为p[i+1]*(1-p[i+1])(第一次不取到的概率乘以第二次取到的概率)
······
后面的第r-j句话取到了地雷,那么概率为p[i+1]*(1-p[i+1])^(r-j-1)
用等比数列求和可推出上述概率之和即为1-(1-p[i+1])^(r-j)。

Notice

没什么坑点,就是根本想不到啊。

Code

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define sqz main
#define ll long long
#define reg register int
#define rep(i, a, b) for (reg i = a; i <= b; i++)
#define per(i, a, b) for (reg i = a; i >= b; i--)
#define travel(i, u) for (reg i = head[u]; i; i = edge[i].next)
const int INF = 1e9, N = 2000000;
const double eps = 1e-6, phi = acos(-1.0);
ll mod(ll a, ll b) {if (a >= b || a < 0) a %= b; if (a < 0) a += b; return a;}
ll read(){ ll x = 0; int zf = 1; char ch; while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
if (ch == '-') zf = -1, ch = getchar(); while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar(); return x * zf;}
void write(ll y) { if (y < 0) putchar('-'), y = -y; if (y > 9) write(y / 10); putchar(y % 10 + '0');}
double f[250][150], g[250][150];
double p[250];
int d[250];
int sqz()
{
	int H_H = read();
	while(H_H--)
	{
		int n = read(), r = read();
		rep(i, 1, n) scanf("%lf%d", &p[i], &d[i]);
		rep(i, 0, n)
			rep(j, 0, r)
				f[i][j] = 0, g[i][j] = 0;
		f[0][0] = 1;
		rep(i, 0, n)
		{
			double pp = 1;
			per(j, r, 0)
			{
				f[i + 1][j] += f[i][j] * pp, f[i + 1][j + 1] += f[i][j] * (1 - pp);
				g[i + 1][j] += g[i][j] * pp, g[i + 1][j + 1] += (g[i][j] + f[i][j] * d[i + 1]) * (1 - pp);
				pp *= 1 - p[i + 1];
			}
		}
		double ans = 0;
		rep(i, 0, r) ans += g[n][i];
		printf("%.10lf\n", ans);
	}
}
posted on 2017-09-27 20:23  WizardCowboy  阅读(179)  评论(0)    收藏  举报