P3239
P3239
由于期望的好性质,容易想到拆贡献,假设第i张牌出掉的概率是\(f(i)\)那么答案就是
\[\sum_{i=1}^nf(i)d(i)
\]
问题在于怎么求,容易想到dp
记\(dp_{i,j}\)表示前i轮出掉j的概率,转移怎么转?
因为直接考虑每一轮的贡献,那么会受到前一轮影响,并且由于不容易知道前几轮用掉的卡牌是哪些,所以并不好做。
对于这种中间过程操作方式比较复杂度(比如提前终止,隔随机个做一个),考虑对终态计算
显然dp不是\(f(i)\),模拟样例。
第一个选取概率,正难则反
\[f(1)=1-(1-p_1)^r
\]
第二个选取概率,分情况
- 第一个死活不选
\((1-p_1)^{r}(1-(1-p_2)^{r})\) - 第一个选了
\(f(1)(1-(1-p_2)^{r})\)
发现有一些规律,后面的概率,和前面选多少数有关
记\(dp_{i,j}\)表示前i个数选j个的概率。
分类转移:
- 第i+1个不选,\(dp_{i,j}(1-p_{i+1})^{r-j}\)->\(dp_{i+1,j}\)
- 第i+1个选,\(dp_{i,j}(1-(1-p_{i+1}))^{r-j}\)->\(dp_{i+1,j+1}\)
那么
\(f(i)=\sum_{j=1}^{min(r,i-1)}dp_{i-1,j}(1-(1-p_i)^{r-j})\)
code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define P pair<ll,ll>
#define ld long double
void rd(ll& x)
{
char ch=getchar();
ll f=1;x=0;
while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+ch-48;
ch=getchar();
}
x=x*f;
}
const ll N_ = 300;
ld ans,f[N_],dp[N_][N_],p[N_],d[N_];
ll T,N,R;
ld qp(ld x,ll y)
{
ld res=1;
while(y)
{
if(y&1)res=res*x;
x=x*x;
y>>=1;
}
return res;
}
inline void clear()
{
for(int i=1;i<=N;i++)f[i]=0;
memset(dp,0,sizeof(dp));
ans=0;
}
int main()
{
cin>>T;
while(T--)
{
cin>>N>>R;
for(int i=1;i<=N;i++)cin>>p[i]>>d[i];
dp[0][0]=1;
for(int i=0;i<N;i++)
for(int j=0;j<=min(R,i*1ll);j++)
{
dp[i+1][j+1]+=dp[i][j]*(1-qp(1-p[i+1],R-j));
dp[i+1][j]+=dp[i][j]*qp(1-p[i+1],R-j);
}
for(int i=1;i<=N;i++)
for(int j=0;j<=min(i*1ll-1,R);j++)
f[i]+=dp[i-1][j]*(1-qp(1-p[i],R-j));
for(int i=1;i<=N;i++)ans+=f[i]*d[i];
printf("%.10Lf\n",ans);
clear();
}
return 0;
}
小结:1.终态的计算。2.无思路模拟样例

浙公网安备 33010602011771号