CF908D题解
题意
一个空串,每次操作 \(p_a\) 概率加入a
\(p_b\) 概率加入 b
,当子序列 ab
(不必连续)个数 \(\ge k\) 时停止,问最终字符串中子序列 ab
的期望个数。
\(k \le 1000\)
题解
思路
首先发现字符串长度无限制,不能用传统总和除以总方案来算期望。
研究字符串形成方案,发现:
每个字符串最后一定是加 \(m\) 个
a
,然后加入 \(1\) 个b
。
设之前有 \(cnt\) 个ab
, \(n\) 个 \(a\) ,最终字符串的ab
个数就是\(cnt+n+m\) ,而与之前具体状态无关。因此只要知道之前有多少个 a
和 ab
和此时的概率,然后枚举一下 \(m\) 算一下即可,奶一口最后可以用和式化掉。
做法
\(f_{i,j}\) 表示有 \(i\) 个 a
和 \(j\) 个 ab
的概率,因为最开始加一堆 b
没意义,直接钦定第一个为 a
。而为了不重,我们枚举添加最后一个 b
之前的状态。
\[f_{i,j}=f_{i-1,j}\times p_a + f_{i,j-i} \times p_b\\
f_{1,0} = 1\\
Ans=\sum\limits_{i=1}^{+\infty}\sum_{i+j\ge k}^{k-1} f_{i,j} \times p_b \times (i+j)
\]
第一维没有上界没法做,发现:
在最后一次加
aa...ab
之前,a
的个数一定小于 \(k\) 。
对于这一部分暴力算。对于 \(i\ge k\) ,我们枚举之前 ab
个数,记为 \(j\) ,然后枚举总共有 \(k+i\) 个a
,贡献为:
\[\begin{align*}
&\quad\ \ f_{k,j}\times\sum\limits_{i=0}^{+\infty} (p_a)^i \times p_b \times (i+j+k)\\
&=f_{k,j}\times p_b\times((j+k)\times\sum\limits_{i=0}^{+\infty} (p_a)^i+\sum_{i=0}^{+\infty}i\times (p_a)^i)\\
&=f_{k,j}\times p_b\times (\frac{j+k}{1-p_a}+\frac{p_a}{(1-p_a)^2})\\
&=f_{k,j}\times p_b\times(\frac{j+k}{p_b}+\frac{p_a}{(p_b)^2})\\
&=f_{k,j}\times (j+k+\frac{p_a}{p_b})
\end{align*}
\]
最后答案就是
\[Ans=\sum\limits_{i=1}^{k-1}\sum_{j=k-i}^{k-1} f_{i,j} \times p_b \times (i+j)+\sum_{i=0}^{k-1} f_{k,i}\times(i+k+\frac{p_a}{p_b})
\]
Code
#include<bits/stdc++.h>
#define ri register int
#define ll long long
using namespace std;
const int maxn = 1e3 + 10,mod = 1e9 + 7;
inline ll qp(ll x,int k){
ll res = 1;
while(k){
if(k & 1) res = res * x % mod;
x = x * x % mod;
k >>= 1;
}
return res;
}
ll f[maxn][maxn];
int pa,pb,s,k;
int main(){
scanf("%d%d%d",&k,&pa,&pb);
s = pa + pb;
pa = pa * qp(s,mod - 2) % mod,pb = pb * qp(s,mod - 2) % mod;
ll inv = pa * qp(pb,mod - 2) % mod;
f[1][0] = 1;
for(ri i = 1;i <= k;++i)
for(ri j = 0;j < k;++j){
if(i == 1 && j == 0) continue;
f[i][j] = f[i-1][j] * pa % mod;
if(j >= i) f[i][j] = (f[i][j] + f[i][j-i] * pb % mod) % mod;
}
ll ans = 0;
for(ri i = 1;i < k;++i)
for(ri j = k-i;j < k;++j)
ans = (ans + f[i][j] * pb % mod * (i+j) % mod) % mod;
for(ri i = 0;i < k;++i) ans = (ans + f[k][i] * (i+k+inv) % mod) % mod;
printf("%lld\n",ans);
return 0;
}
总结
这道题区别于套路期望计数,需要发现题目的性质然后设出 \(DP\) ,再发现性质分类讨论得到最后的式子。更加灵活、偏向思维,而不是直接生成函数化式子。在两个和式中对于基本功也有考察。