题目链接:bzoj4008
对于答案的计算有一个很显然的思路,记第(i)张卡牌在游戏中被发动的概率为(f_i),则(Ans=sum_{i=1}^nf_id_i)
考虑如何计算(f_i),可以用(1-)不发动这张卡牌的概率
不发动这张卡牌的概率(=(1-p_i)^{它被考虑的轮数}*1^{它不被考虑的轮数}*该局面出现的概率)
对于最后一项可以考虑dp,记(dp_{i,j})表示前(i)张卡牌中发动(j)张的概率,按照当前是否发动第(i)张进行转移
1)发动第(i)张,那么首先(j>0),其次在(j-1)轮中,由于发动了(i)之前的卡牌故不会考虑(i),所以这种情况对(dp_{i,j})的贡献为(dp_{i-1,j-1}*(1-(1-p_i)^{r-(j-1)}))
2)不发动第(i)张,和第一种同理,对答案贡献为(dp_{i-1,j}(1-p_i)^{r-j}),注意此时(i eq j)
综合起来就是
[dp_{i,j}=dp_{i-1,j-1}*(1-(1-p_i))^{r-(j-1)}+dp_{i-1,j}*(1-p_i)^{r-j}
]
好了现在(f_i)的计算就比较轻松了,枚举在(i)之前选了(j)张卡牌,那么在这(j)轮是不会考虑(i)这一张卡牌的,故
[f_i=sum_{j=0}^{min(i-1,r)}dp_{i-1,j}(1-(1-p_i)^{r-j})
]
预处理((1-p_i)^j)以加速运算
#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
using namespace std;
int n,m,r,d[1010],t;
double p[1010],powe[1010][1010],dp[1010][1010],f[1010];
int read()
{
int x=0,f=1;char ch=getchar();
while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
return x*f;
}
void init()
{
n=read();r=read();
memset(f,0.0,sizeof(f));
memset(dp,0.0,sizeof(dp));
int i,j;
for (i=1;i<=n;i++) scanf("%lf%d",&p[i],&d[i]);
for (i=1;i<=n;i++)
{
powe[i][0]=1.0;
for (j=1;j<=r;j++) powe[i][j]=powe[i][j-1]*(1-p[i]);
}
}
void work()
{
int i,j;
dp[1][1]=1.0-powe[1][r];dp[1][0]=powe[1][r];
for (i=2;i<=n;i++)
{
for (j=0;j<=min(i,r);j++)
{
if (j) dp[i][j]+=dp[i-1][j-1]*(1.0-powe[i][r-j+1]);
if (i!=j) dp[i][j]+=dp[i-1][j]*powe[i][r-j];
}
}
}
void out()
{
double ans=0.0;f[1]=1-powe[1][r];
int i,j;
for (i=2;i<=n;i++)
{
for (j=0;j<=min(i-1,r);j++)
f[i]+=dp[i-1][j]*(1.0-powe[i][r-j]);
}
for (i=1;i<=n;i++) ans+=f[i]*d[i];
printf("%0.10lf
",ans);
}
int main()
{
t=read();
while (t--)
{
init();
work();
out();
}
return 0;
}