CF908D New Year and Arbitrary Arrangement(期望dp+数学)

Let dp[i][j] be the expected answer given that there are i subsequences of the form ‘a’ in the prefix and j subsequences of the form ‘ab’ in the prefix.

Then, we have something like dp[i][j] = (pa * dp[i + 1][j] + pb * dp[i][i + j]) / (pa + pb).The first term in this sum comes from adding another ‘a’ to our sequence, and the second comes from adding a ‘b’ to our sequence. We also have dp[i][j] = j if j ≥ k as a base case.
The answer should be dp[0][0].

官方题解说的比较清楚了。dp[i][j]表示前缀有i个a,j个ab的期望个数。
则显然有转移: dp[i][j]=(padp[i+1][j]+pbdp[i][i+j])/(pa+pb).
答案就是dp[0][0]。
问题在于边界如何确定。首先我们根据题意有 dp[i][j]=j,j>=k
序列中可能会有任意多个a,因为我们可以一直往里扔a,不扔b。
那么我们的边界显然不能只限定j。我们发现当i+j>=k时,只要再出现一个b,这个序列就一定会结束。因此当i+j>=k时,我们手动算一下期望个数,作为边界。还有一个问题,在出现第一个a之前,我可能会有无限个b。但是我们发现,这些b对答案完全没有影响。或者我们可以列出一个方程:
dp[0][0]=(padp[1][0]+pbdp[0][0])/(pa+pb)
dp[0][0]就等于dp[1][0]!所以答案就是dp[1][0]即可。
那么如何计算i+j>=n时的期望个数呢?记个数为ANS,i+j=k,则:
ANS=pbpa+pbj=0inf(papa+pb)j(k+j)
拿等比数列什么的算一算可以得到:
ANS=k+pa/pb ,即i+j+pa/pb。
因此我们就可以dp了。复杂度 O(n2)

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 1100
#define mod 1000000007
inline 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;
}
int n,pa,pb,invb,invab,f[N][N];//f[i][j]表示前缀有i个a,j个ab的期望个数
inline int ksm(int x,int k){
    int res=1;
    for(;k;k>>=1,x=(ll)x*x%mod)
        if(k&1) res=(ll)res*x%mod;return res;
}
inline int dp(int i,int j){
    if(j>=n) return j;
    if(i+j>=n) return (i+j+(ll)pa*invb)%mod;
    if(f[i][j]!=-1) return f[i][j];
    f[i][j]=((ll)pa*dp(i+1,j)+(ll)pb*dp(i,i+j))%mod*invab%mod;
    return f[i][j];
}
int main(){
//  freopen("a.in","r",stdin);
    n=read();pa=read();pb=read();memset(f,-1,sizeof(f));
    invb=ksm(pb,mod-2);invab=ksm(pa+pb,mod-2);
    printf("%d\n",dp(1,0));
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值