CF908D New Year and Arbitrary Arrangement(期望 dp)
CF908D New Year and Arbitrary Arrangement(期望 dp)
题目大意
给定三个数 \(k,pa,pb\)。
每次有 \(\dfrac{pa}{pa+pb}\) 的概率往后面添加一个 a
。
每次有 \(\dfrac{pb}{pa+pb}\) 的概率往后面添加一个 b
。
当出现了 \(k\) 个形如 \(ab\) 的子序列(不用连续)时停止。
求最后 ab
序列的期望数。 答案对 \(10^9+7\) 取模。
Translated by yybyyb
数据范围
\[1 \le k \le 1000
\]
解题思路
切了这道题感觉自信++,好像事和别人不一样的正推方法呢
考虑动态规划,容易发现无限放 a 和 b 的情况不是很好处理。但是!我们就是要正着推!
首先容易发现开头放多少个 b 对答案都没有影响,所以我们钦定第一个放 a。
设 \(f[x][y]\) 表示放了 x 个 a,获得了 y 个子序列的概率,枚举下一个放 a 还是 b 有
\[f[x+1][y] += f[x][y] \times P_a\\
f[x][y+x] += f[x][y] \times P_b
\]
那么当 \(y + x \ge k\) 时,直接把概率乘上 \(y+x\) 加到答案上。
还有一个问题,当 dp 到 \(f[k][x]\) 时,显然再加入一个 b 都会满足条件,但是仍然存在可能使得无限放 a 的可能,所以我们稍微推一下式子即可,枚举放了几个 a,有
\[E(x)=\sum_{i=0}P_a^i P_b\times(k+i)\\
=k+P_b\times\sum_{i=0}P_a^i\times i
\]
如何计算 \(P_a^i\times i\) 可以考虑等比数列求和的基本知识。
最后算出得 \(E(x) = k -1 + \frac 1{P_b}\)
如果你想看等比数列求和基本知识可以继续阅读
\[\text{设 } T= \sum_{i=0}P_a^i\times i\\
E(x)=k+(1-P_a)T\\
P_aT=\sum_{i=1}P_a^i(i-1)\\
T-P_aT=\sum_{i=1}P_a^i=\frac {1}{1-P_a}-1=\frac {1}{P_b}-1\\
\therefore E(x)=k+(1-P_a)T=k-1+\frac {1}{P_b}
\]
代码就很短了。
/*
/> フ
| _ _|
/`ミ _x 彡
/ |
/ ヽ ?
/ ̄| | | |
| ( ̄ヽ__ヽ_)_)
\二つ
*/
#include <queue>
#include <vector>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MP make_pair
#define ll long long
#define fi first
#define se second
using namespace std;
template <typename T>
void read(T &x) {
x = 0; bool f = 0;
char c = getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=1;
for (;isdigit(c);c=getchar()) x=x*10+(c^48);
if (f) x=-x;
}
template<typename F>
inline void write(F x, char ed = '\n') {
static short st[30];short tp=0;
if(x<0) putchar('-'),x=-x;
do st[++tp]=x%10,x/=10; while(x);
while(tp) putchar('0'|st[tp--]);
putchar(ed);
}
template <typename T>
inline void Mx(T &x, T y) { x < y && (x = y); }
template <typename T>
inline void Mn(T &x, T y) { x > y && (x = y); }
const int P = 1e9 + 7;
ll fpw(ll x, ll mi) {
ll res = 1;
for (; mi; mi >>= 1, x = x * x % P)
if (mi & 1) res = res * x % P;
return res;
}
const int N = 2005;
ll k, pa, pb;
ll f[N][N];
int main() {
read(k), read(pa), read(pb);
ll t = fpw(pa + pb, P - 2);
pa = pa * t % P, pb = pb * t % P;
f[1][0] = 1; ll ans = 0;
for (int i = 1;i < k; i++) {
for (int j = 0;j < k; j++) {
f[i+1][j] = (f[i+1][j] + f[i][j] * pa) % P;
if (j + i < k) f[i][j+i] = (f[i][j+i] + f[i][j] * pb) % P;
else ans = (ans + (j + i) * f[i][j] % P * pb) % P;
}
}
t = fpw(pb, P - 2) * pa % P;
for (int i = 0;i < k; i++)
ans = (ans + f[k][i] * (k + i + t)) % P;
write(ans);
return 0;
}