题目:
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;
}
最后请允许蒟蒻庆祝一波攻克这个题