Codeforces 908D New Year and Arbitrary Arrangement

19 篇文章 0 订阅
18 篇文章 0 订阅

题目:
http://codeforces.com/problemset/problem/908/D

题意:

给定字符’a’出现的概率pa/(pa+pb),’b’出现的概率pb/(pa+pb);
给定一个正整数k,构造一个字符串,当出现至少k个子序列”ab”时停止构造;
问子序列”ab”出现次数的期望P/Q,输出P*(Q的逆元);
注意是子序列不是子串ab。

分析:

这个题特别难想!
一个 (dp+费马小定理+数学+思维) 题;
由费马小定理得,a在模mod下的逆元是a^(mod-2);
令pa+pb的逆元是inv;
令 f[i][j] 表示 i 个a,j个ab 时候的概率;
那么容易得到

f[i+1][j] = f[i][j] * pa * inv;(在后面放一个a)
f[i][i+j] = f[i][j] * pb *inv;(在后面放一个b)

但是,注意到这个串有可能无限长;
那么,我们考虑i+j>=k的情况,
在这种情况下,只要后面再出现一个b,我们立刻就可以停止,
当后面出现:

(这里暂且用pb表示b出现的概率,不考虑inv)
0个a,1个b:得到i+j个ab,答案加(i+j)*f[i][j]*pb;
1个a,1个b:得到i+j+1个ab,答案加(i+j+1)*f[i][j]*pa*pb;
2个a,1个b:得到i+j+2个ab,答案加(i+j+2)*f[i][j]*(pa^2)*pb;
3个a,1个b:得到i+j+3个ab,答案加(i+j+2)*f[i][j]*(pa^3)*pb;
...
n个a,1个b:得到i+j+n个ab,答案加(i+j+n)*f[i][j]*(pa^n)*pb;
汇总一下,答案加上f[i][j]*pb*∑(i+j+n)*(pa^n)
(这个有人说可以用无穷级数求和,但是我还没学到,这里用等差*等比数列求和算,再写一个式子相减即可)
f[i][j]*pb* ∑(i+j+n)*(pa^n)
=f[i][j]*pb* [i+j+pa*(1-pa^n)/pb]/pb
=f[i][j]*[i+j+pa*(1-pa^n)/pb]
→f[i][j]*[i+j+pa/pb]
即f[i][j]*[i+j+pa*inv(pb)]

总结:

这种dp不输出特定的某一个dp值(如f[n]),而是在dp过程中统计答案;
注意用费马小定理+快速幂求逆元的方法;
注意dp的隐含边界条件,以及如何用极限处理无限长度的问题。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int Tmax=1005;
const ll MOD=1e9+7;
int k;
ll pa,pb,f[Tmax][Tmax],ans,inv; //f[i][j] i ge a , j ge ab
ll quickpower(ll base,ll m)
{
    ll ret=1;
    while(m>0)
    {
        if((m&1)==1)
          ret=ret*base%MOD;
        base=base*base%MOD;
        m>>=1;
    }
    return ret;
}
int main()
{
    int i,j;
    cin>>k>>pa>>pb;
    inv=quickpower(pa+pb,MOD-2);
    f[1][0]=1;
    for(i=1;i<=k;i++)
      for(j=0;j<=k;j++)
      {
         if(i+j>=k)
            ans=(ans+f[i][j]*(i+j+pa*quickpower(pb,MOD-2)%MOD)%MOD)%MOD;
         else
         {
            f[i+1][j]=(f[i+1][j]+f[i][j]*pa%MOD*inv%MOD)%MOD;
            f[i][j+i]=(f[i][j+i]+f[i][j]*pb%MOD*inv%MOD)%MOD;
         }
      }
    cout<<ans;
    return 0;
}

最后请允许蒟蒻庆祝一波攻克这个题
这里写图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值