观察题目,我们会发现两个性质:
-
一张卡片最多只能在一轮游戏中被成功使用。
-
一轮游戏最多只能成功使用一张卡片。
这样,我们纵向考虑每一张卡片,判断它在某局游戏中被成功使用的概率。
设我们当前有\(t\)轮游戏,且该卡片成功概率是\(p\)。则我们有\((1-p)^t\)的概率在这局游戏中没有使用它,其余情况则在某局游戏中成功使用。
现在考虑这个\(t\)是怎么来的。显然,假如前面的卡片中有\(s\)张被使用了,这就相当于占用了\(s\)轮,在剩下的\(r-s\)轮里该卡片才有可能被使用。故此时我们有\(t=r-s\)。
我们设\(f[i][j]\)表示前\(i\)张卡片里,恰有\(j\)张卡片被成功施放的概率。则我们有
我们有
的概率在所有游戏中成功施放该卡片。故直接用此概率与造成的伤害求积即可得到期望。
复杂度\(O(Tnr\log r)\),假如你用快速幂的话。尽管倒着计算可以将复杂度优化至\(O(Tnr)\),但是上述复杂度已经可以通过。
代码:
#include<bits/stdc++.h>
using namespace std;
double ksm(double p,int q){
double r=1;
for(;q;q>>=1,p=p*p)if(q&1)r=p*r;
return r;
}
int T,n,m,a[300];
double p[300],res,f[300][300];
int main(){
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
for(int i=0;i<=n;i++)for(int j=0;j<=m;j++)f[i][j]=0;
for(int i=1;i<=n;i++)scanf("%lf%d",&p[i],&a[i]);
f[0][0]=1,res=0;
for(int i=1;i<=n;i++)for(int j=0;j<=min(m,i-1);j++){
double pos=ksm(1-p[i],m-j);
f[i][j]+=f[i-1][j]*pos;
f[i][j+1]+=f[i-1][j]*(1-pos);
res+=(1-pos)*f[i-1][j]*a[i];
}
printf("%.10lf\n",res);
}
return 0;
}