CF908D New Year and Arbitrary Arrangement 题解
upd 2024.2.18 修改笔误
0. 前言
有一天 Au 爷讲期望见到了此题,通过写题解来加深理解。
1. 题意
将初始为空的序列的末尾给定概率添加
2. 思路
通过查看标签 通过阅读题面我们容易发现本题是一道期望 DP,但是本题的状态并不很容易想到,设
若
故终止状态为:
3. 解释
(本块主要针对
我一直疑惑
解释一下,在前缀有了
接下来的证明部分参考一粒夸克的博客
首先是等差乘等比数列求和公式
将公式代入无限和式
(这么巨量
4. 细节
- 由于
f[0][0] 会转移到自己,递归记忆化会死循环,从f[1][0] 开始算,当序列前有一堆b 的情况没有意义,可以跳到第一个a 发生时开始算。初始状态选取f[1][0] 。 - 当
a 与ab 的个数相加已经大于k 了,这时就不关心有多少a 了,只需要有一个b 就可以结束了,这样可以把两维都控制在O(k) 的复杂度
5. 代码
这是一份逆推实现的代码:
#include<map>
#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
template<class T> inline void read(T &x){
x = 0; register char c = getchar(); register bool f = 0;
while(!isdigit(c)) f ^= c == '-', c = getchar();
while(isdigit(c)) x = x * 10 + c - '0', c = getchar();
if(f) x = -x;
}
template<class T> inline void print(T x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) print(x / 10);
putchar('0' + x % 10);
}
const int N = 1010;
const int mod = 1e9 + 7;
int n, pa, pb, A, B, C;
int f[N][N];
inline int qpow(int a, int b){
int res = 1;
while(b){
if(b & 1) res = 1ll * res * a % mod;
a = 1ll * a * a % mod, b >>= 1;
}
return res;
}
inline int work(int x){
return qpow(x, mod - 2);
}
signed main(){
read(n), read(pa), read(pb);
A = 1ll * pa * work(pa + pb) % mod;
B = 1ll * pb * work(pa + pb) % mod;
C = 1ll * pa * work(pb) % mod;
for(int i = n; i >= 1; --i)
for(int j = n; j >= 0; --j){
if(i + j >= n) f[i][j] = (i + j + C) % mod;
else f[i][j] = (1ll * A * f[i + 1][j] % mod + 1ll * B * f[i][j + i] % mod) % mod;
}
print(f[1][0]), puts("");
return 0;
}
这是一份记搜实现的代码片段:
inline int dp(int i, int j){
if(i + j >= k) return (i + j + C) % mod;
if(~ f[i][j]) return f[i][j];
return (1ll * A * dp(i + 1, j) + 1ll * B * dp(i, j + i)) % mod;
}