题目链接:Problem - 908D - Codeforces
题意:给定三个整数k,pa,pb,一开始串为空,每次有pa/(pa+pb)的概率向串的末尾加一个a,有pb/(pa+pb)的概率向串的末尾加一个b,当串中ab的子序列个数大于等于k时就停止加字符,问停止加字符后串中ab子序列的个数的期望。一般求出是一个最简分数ans1/ans2,输出ans1乘以ans2关于mod的逆元再对mod取模。mod为1e9+7
分析:设f[i][j]表示当前串中有i个a,j个ab子序列时加字符直到停止时ab子序列的个数的期望。
那么显然有f[i][j]=j(j>=k),动态转移方程也比较简单,就是:
f[i][j]=(pa/(pa+pb))*f[i+1][j]+(pb/(pa+pb))*f[i][j+i]
当我们此次操作是向原串中加入一个a时,对原串的影响只是a的个数加1,并不会对原串中ab子序列的个数产生影响,而当我们此次操作是向原串中加入一个b时,对原串中a的个数并没有影响,但原串中有多少个a,那么和当前操作加入的b组合后就会多出来几个ab子序列,这也就是动态转移方程的由来。
但是我们容易发现一个问题,就是如果字符串中一直添加的是a,那么根本就不会停止,所以说我们还需要手算一些a特别多的情况:
当a的个数+ab子序列的个数大于等于k时,那么我们现在只需要在后续的字符选择中选择一个b,那么就可以满足题意了,所以我们可以手算这种情况下的期望值。
设p1=pa/(pa+pb) p2=pb/(pa+pb)
假设我们第k次时选到了一个b,那么这种情况发生的概率就是
p1^(k-1)*p2,这种情况下最后ab子序列的数目是|a|+(k-1)+|ab|,那么期望就是
我们的目标状态就是f[1][0],因为一定需要先有一个a才可能会出现ab子序列,所以我们可以直接输出f[1][0],如果我们输出f[0][0]的话会出现一个问题,因为在(0,0)状态还可能转换到(0,0)状态,而这样会无限递归而且永远不会满足递归终止条件,所以我们应该输出f[1][0]。
下面是代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;
const int N=1e3+10,mod=1e9+7;
long long qpow(long long a,long long b)
{
long long ans=1;
while(b)
{
if(b&1) ans=ans*a%mod;
b>>=1;
a=a*a%mod;
}
return ans;
}
long long pa,pb,k,ni;
long long f[N][N];//f[i][j]表示当前状态有i个a,j个ab子序列时加字符直到停止的期望ab个数
long long dfs(long long x,long long y)//X代表当前状态a的个数,y代表当前状态ab子序列的个数
{
if(x+y>=k) return (x+y-1+(pa+pb)*qpow(pb,mod-2))%mod;
if(f[x][y]>0) return f[x][y];
f[x][y]=(f[x][y]+pa*ni%mod*dfs(x+1,y))%mod;
f[x][y]=(f[x][y]+pb*ni%mod*dfs(x,x+y))%mod;
return f[x][y];
}
int main()
{
cin>>k>>pa>>pb;
ni=qpow(pa+pb,mod-2);
printf("%lld",dfs(1,0));
return 0;
}