「CF908D」New Year and Arbitrary Arrangement - 期望 + 动态规划

给出正整数pa,pb,kp_a,p_b,k

一开始有一个空串,每一次有papa+pb\frac{p_a}{p_a+p_b}的概率在末尾加入aapapa+pb\frac{p_a}{p_a+p_b}的概率在串末尾加入bb

当串中存在kk个子序列abab时停止,求停止时子序列abab的期望个数,答案对109+710^9+7取模

CF908D

Solution

注:以下所有的pap_a表示原题中的papa+pb\frac{p_a}{p_a+p_b}pbp_b表示 pbpa+pb\frac{p_b}{p_a+p_b}

显然子序列abab的个数只与当前已经有多少个aa和已经出现的子序列abab的个数有关

那么首先有一个很简单的dp:dp[i][j]dp[i][j]表示已经出现了iiaajj个子序列abab,到结束时子序列abab个数的期望。

考虑往后加的是aa还是bb,很容易得到转移方程:

dp[i][j]=dp[i+1][j]pa+dp[i][i+j]pbdp[i][j] = dp[i + 1][j] * p_a + dp[i][i + j] * p_b

但是我们会发现终止状态,也就是边界情况并不好处理,因为这个dp会无限进行下去

仔细思考一下,使得它无限进行下去的原因是这样一种情况:到后面aa的个数很多但bb很少,可能加上一个bb就会停止了,但一直加的是aa

再提炼一下,发现就是当i+jki+j\ge k时很不好考虑

因此我们对于i+jki+j\ge k时,直接计算dpdp值,因为此时只要再添一个bb就能终止

S=dp[i][j](i+jk)S = dp[i][j](i+j\ge k),考虑这个bb什么时候被添上,那么就有:

S=(i+j)pb+(i+j+1)papb+(i+j+2)pa2pb+...=pbx=0(i+j+x)pax\begin{aligned} S &= (i + j)p_b + (i + j + 1)p_ap_b + (i+j+2)p_a^2p_b+...\\\\ &=p_b\sum_{x=0}^{\infty}(i+j+x)p_a^x \end{aligned}

接下来的套路就和这道题类似了

paS=pbx=0(i+j+x)pax+1p_aS = p_b\sum_{x=0}^{\infty}(i+j+x)p_a^{x+1}

两式相减得到:

其中x=1pax\sum_{x=1}^{\infty}p_a^x就是无穷等比数列求和,不难发现它就是papa1pa=papa1=papb\frac{p_a^{\infty} - p_a}{1 - p_a} = \frac{p_a}{p_a - 1} = \frac{p_a}{p_b}

所以当i+jki+j\ge k时,dp[i][j]=i+j+papbdp[i][j] = i + j + \frac{p_a}{p_b}

Summary

在dp边界是\infty时可以考虑把后面的某些dp值根据其具体意义直接计算

要掌握这种通过差分解决无穷概率的问题

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <bits/stdc++.h>

#define x first
#define y second
#define y1 Y1
#define y2 Y2
#define mp make_pair
#define pb push_back

using namespace std;

typedef long long LL;
typedef pair <int, int> pii;

template <typename T> inline int Chkmax (T &a, T b) { return a < b ? a = b, 1 : 0; }
template <typename T> inline int Chkmin (T &a, T b) { return a > b ? a = b, 1 : 0; }
template <typename T> inline T read ()
{
T sum = 0, fl = 1; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') fl = -1;
for (; isdigit(ch); ch = getchar()) sum = (sum << 3) + (sum << 1) + ch - '0';
return sum * fl;
}

inline void proc_status ()
{
ifstream t ("/proc/self/status");
cerr << string (istreambuf_iterator <char> (t), istreambuf_iterator <char> ()) << endl;
}

const int Maxn = 1e3 + 100, Mod = 1e9 + 7;

int Pa, Pb, K, Div;

inline int Pow (int a, int b)
{
int ans = 1;
for (int i = b; i; i >>= 1, a = (LL)a * a % Mod) if (i & 1) ans = (LL)ans * a % Mod;
return ans;
}

int Dp[Maxn][Maxn];

inline int get_dp (int i, int j)
{
if (i + j >= K) return (i + j + Div) % Mod;
if (Dp[i][j]) return Dp[i][j];
return Dp[i][j] = ((LL)get_dp (i + 1, j) * Pa % Mod + (LL)get_dp (i, i + j) * Pb % Mod) % Mod;
}

inline void Solve ()
{
printf("%d\n", get_dp(1, 0));
}

inline void Input ()
{
K = read<int>(), Pa = read<int>(), Pb = read<int>();
Pa = (LL)Pa * Pow(Pa + Pb, Mod - 2) % Mod;
Pb = (1 - Pa + Mod) % Mod;
Div = (LL)Pa * Pow(Pb, Mod - 2) % Mod;
}

int main()
{
#ifndef ONLINE_JUDGE
freopen("D.in", "r", stdin);
freopen("D.out", "w", stdout);
#endif
Input();
Solve();
return 0;
}
感谢你的赞赏!