III.[HNOI2015]亚瑟王

观察题目,我们会发现两个性质:

  1. 一张卡片最多只能在一轮游戏中被成功使用。

  2. 一轮游戏最多只能成功使用一张卡片。

这样,我们纵向考虑每一张卡片,判断它在某局游戏中被成功使用的概率。

设我们当前有\(t\)轮游戏,且该卡片成功概率是\(p\)。则我们有\((1-p)^t\)的概率在这局游戏中没有使用它,其余情况则在某局游戏中成功使用。

现在考虑这个\(t\)是怎么来的。显然,假如前面的卡片中有\(s\)张被使用了,这就相当于占用了\(s\)轮,在剩下的\(r-s\)轮里该卡片才有可能被使用。故此时我们有\(t=r-s\)。

我们设\(f[i][j]\)表示前\(i\)张卡片里,恰有\(j\)张卡片被成功施放的概率。则我们有

 

\[f[i][j]=f[i-1][j]\times(1-p_i)^{r-j}+f[i-1][j-1]\times\Big(1-(1-p_i)^{r-j+1}\Big) \]

 

我们有

 

\[\sum\limits_{j}f[i][j]\times\Big(1-(1-p_i)^{r-j}\Big) \]

 

的概率在所有游戏中成功施放该卡片。故直接用此概率与造成的伤害求积即可得到期望。

复杂度\(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;
}