又是一期关于期望的讨论

13 篇文章 0 订阅
2 篇文章 0 订阅

今天又做到了一道期望dp的题目,然而,还是犯了一个非常经典的错误,总是用正向推导去做期望dp(似乎这样更符合dp的逻辑),但是对于这个情况,期望dp总是会出现不好处理第一次操作和最后一次操作,我们来从两道题目来窥见这个问题吧~

1.2021牛客多校第四场B

题意:玩一个游戏,在第 x x x轮,随机生成一个 [ 1 , n ] [1,n] [1,n]的数 i i i,然后会得到 x 2 x^2 x2的贡献。游戏要想玩下去,每次生成的数必须大于等于前面生成的数,也即为当前的最大值。问期望得到的贡献。

思考:首先观察这道题,先明确一点,就当前而言,是无法使用期望的线性性质,也就是说不能简单的认为: ( x + 1 ) − x = 1 (x+1)-x=1 (x+1)x=1
当前局面的贡献 = ∑ \sum (上一个局面的贡献 + 1) * p
理论上应该是: ( x + 1 ) 2 − x 2 = 2 ∗ x + 1 (x + 1)^2-x^2=2*x+1 (x+1)2x2=2x+1
当前局面的贡献 = ∑ \sum (上一局面的贡献 + 2 * x + 1) * p
而这里这个x随着游戏轮数的变换而变化。
然后大胆猜想这里的 x x x可以用第一个式子来表示。

在考场上的时候,经典的使用了正向去推期望dp,然后死活处理不对。

考虑用逆向dp,令 d p [ i ] dp[i] dp[i]代表当前最大值为 i i i的用 x x x算贡献(第一个式子)到游戏结束的期望期望, f [ i ] f[i] f[i]代表最大值为 i i i的用 x 2 x^2 x2算贡献(第二个式子)到游戏结束的期望贡献。

有:
d p [ i ] = ∑ j = i j = n ( d p [ j ] + 1 ) ∗ p j dp[i] = \sum_{j=i}^{j=n} {(dp[j] + 1)*p_j} dp[i]=j=ij=n(dp[j]+1)pj
f [ i ] = ∑ j = 1 j = n ( f [ j ] + 2 ∗ d p [ i ] + 1 ) ∗ p j f[i] = \sum_{j=1}^{j=n} {(f[j] + 2*dp[i] +1)*p_j} f[i]=j=1j=n(f[j]+2dp[i]+1)pj
最后再计算一下第一次产生数字的贡献即可。

代码不是我写的,所以就不贴了。

2.牛客多校第一场I

题意:一个排列,两个人轮流取数,每次操作需要满足:
当前取数 a i a_i ai,自己上一次取的数 a j a_j aj,满足 i > j i>j i>j
当前取数 a i a_i ai,需要大于两个人前面所有的数
求期望的取数次数

思考:显然可以发现,两个人每次取数的时候,存在大小限制和位置限制。如果不是最初的状态,那么两个人其实没有本质区别。而对于期望dp,很重要的一点是逆向考虑。
不妨定义 d p [ x ] [ y ] dp[x][y] dp[x][y]当前为此人取数,且他上一次取的数为 x x x,另一个人上一次取数为 y y y。也就是说,在不考虑人的差别的情况下,上上次的取数为 x x x,上一次的取数为 y y y
思考一下可以发现,
d p [ x ] [ y ] = ∑ i = 1 i = t o t ( d p [ y ] [ b i ] + 1 ) ∗ i n v [ t o t ] = 1 + i n v [ t o t ] ∗ ∑ i = 1 i = t o t d p [ y ] [ b i ] dp[x][y] = \sum_{i=1}^{i=tot} {(dp[y][b_i] +1)*inv[tot]} = 1 + inv[tot] * \sum_{i=1}^{i=tot} {dp[y][b_i]} dp[x][y]=i=1i=tot(dp[y][bi]+1)inv[tot]=1+inv[tot]i=1i=totdp[y][bi]
其中 t o t 为 tot为 tot满足: b i > y b_i > y bi>y p o s [ b i ] > p o s [ x ] pos[b_i]>pos[x] pos[bi]>pos[x]的数的个数。

对于初始状态,第一个人随便取数,第二个人还没取数 d p [ 0 ] [ i ] dp[0][i] dp[0][i],而到达这个局面是有 1 1 1的贡献,所以需要加上。

#include<bits/stdc++.h>
#define For(aa, bb, cc) for(int aa = (bb); aa <= (int)(cc); ++aa)
#define Forr(aa, bb, cc) for(int aa = (bb); aa >= (int)(cc); --aa)
using namespace std;
typedef long long LL;
const int maxn = 5010;
const int mod = 998244353;
int n;
int a[maxn];
int dp[maxn][maxn];
//dp[last_last_val][last_val] now choose the first part
int inv[maxn], pos[maxn], sum[maxn], cnt[maxn];

int qpow(int x, int y){
	int res = 1;
	while(y){
		if(y & 1) res = 1ll * res * x % mod;
		x = 1ll * x * x % mod;
		y >>= 1;
	}
	return res;
}

int main(){
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
#endif
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cin >> n;
	For(i, 1, n){
		cin >> a[i], inv[i] = qpow(i, mod - 2);
		pos[a[i]] = i;
	}
	Forr(i, n, 1){ //val i=y
		//dp[x][y] = \sigma_{i=1}^{i=tot} {(dp[y][a_i]+1)*inv(tot)}
		For(j, 0, n) sum[j] = cnt[j] = 0;
		For(j, i + 1, n){ //val j=a_i > y
			sum[pos[j]] = dp[i][j]; //j = a_k
			cnt[pos[j]] = 1;
		}
		Forr(j, n - 1, 1){ //pos j
			sum[j] = (sum[j] + sum[j + 1]) % mod;
			cnt[j] += cnt[j + 1];
		}
		For(j, 0, i - 1){ //val x=j, pos[a_i] > pos[x]
			if(cnt[pos[j] + 1])
				dp[j][i] = 1ll * inv[cnt[pos[j] + 1]] * sum[pos[j] + 1] % mod + 1;
		}
	}
	LL ans = 0;
	For(i, 1, n) ans = (ans + dp[0][i]) % mod;
	cout << (ans * inv[n] % mod + 1) << endl;
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值