Loading

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.无思路模拟样例

posted @ 2026-07-27 18:42  leaf_ydc  阅读(8)  评论(0)    收藏  举报