(期望dp)CF1866M Mighty Rock Tower

HZOJ
洛谷

写在前面

岛屿 是的 这里是岛屿 我们彼此造就的小岛,
不错 forever young 永远这两个字不过是转瞬即逝的沙堡,
诀别仿佛灾难警报,
与想念一同迎来的早晨,
我们都度过了这永恒的劫难,
一定要在这座岛屿上再次相遇,
正如谁安慰我所说的那句,
回忆不过丝缕 可要想忘掉还真没那么容易,
纵然时光流逝,
那个地方却依旧紧紧攥住我

CF1866M Mighty Rock Tower 题解

观前提示:本文就是一坨屎,可能一定含有巨量错误或者描述不清的地方,建议别看。

题意

给定一个百分概率的整数数组\(p[i]\),代表从该下标高度为起点,每个高度往下掉石头的概率,特别地,只有当上层的石头掉完了该层的石头才可能掉。求问从第0层开始往上堆石头,堆到第\(n\) 层的期望步数,输出期望值的最简整数比在模998244353意义下的答案。

思路

数学恐惧综合征患者狂喜(因为疯了)。首先忘记了期望咋算。。。。。。翻遍各种资料也没想起来,那就硬着头皮按记忆中的东西写吧。最简单的想法就是设状态\(f[i]\) 为0到第\(i\) 层的期望步数。比如\(p[1]=80%\),那么每一步对\(f[1]\) 的贡献就为\(0.2\),走1步的期望步数即为5。但是本题中这种状态具有后效性,因为石头可以从更高的地方掉到第1层,无法正确统计答案,故考虑换种思路。根据对若干篇题解的研究,发现将状态定义为从第\(i-1\) 层走到第\(i\) 层的期望步数,这种定义方式只用考虑前面答案对该位置答案的影响没有后效性,而且最后统计答案也方便:\(ans=\sum_{i=1}^n f[i]\)

接下来就是漫长而又痛苦的推式子环节。由于我太废物了只能综合多篇题解理解了。

设当前在第\(i\) 层。当一次就成功了对答案的贡献是1,考虑其他情况。
1.当石头掉到了第0层,则该种情况对答案的贡献即为掉到第0层的概率乘上从第0层到第\(i\) 层的期望步数,即为:

\[ p_i^i \sum_{j=1}^i \]

2.当石头掉到了非零层,参考上面的求法,能得到类似的式子,只不过要多乘上一项:因为到达第\(i\)层不能掉。所以式子为:

\[ \sum_{j=1}^{i-1}p_i^{i-j} (1-p[i])\sum_{k=j+1}^i f[i] \]

所以综合以上情况式子就为:

\[ f[i]=1+p_i^i \sum_{j=1}^i+\sum_{j=1}^{i-1}p_i^{i-j} (1-p[i])\sum_{k=j+1}^i f[i] \]

由于等式右边也存在\(f[i]\),考虑化简。化简的思路大概就是拆项合并项,将所有求和项拆开合并为同起点同终点。大概流程可以参考这篇

最终化简后的式子即为:

\[ f[i]=\frac{1+\sum_{j=0}^{i=2}p_i^{i-j}f[j+1]}{1-p[i]} \]

但是本题过于良心,到这里还不让你过。明显看出到目前为止求解的复杂度为\(O(n^2)\) 无法通过。在题解的指引下观察数据范围,\(p[i]\) 的数据范围竟然只有\([0,99]\),而且上式求和一项具有单调性,故可以在计算\(f\)答案时维护求和式的前缀和,设\(qzh[j]\) 为百分率为该值时的和。很简单地推出滚动优化后的式子:

\[ qzh[j]=j\times(qzh[j]+j\times f[i]) \]

所以本题就解决了。

Warnings

记得取模。而且不能直接大除特除,应该先预处理逆元后相乘。

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10,mod=998244353;
int n,p[maxn],f[maxn],qzh[100],ll[maxn];
inline int qpow(int x,int y){
	if(!x) return 1;
	int res=1;
	while(y){
		if(y&1) res=1ll*res*x%mod;
		x=1ll*x*x%mod;
		y>>=1;
	}
	return res;
}

int main(){
	std::ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++) cin>>p[i];
	for(int i=0;i<=99;i++) ll[i]=1ll*i*qpow(100,mod-2)%mod;
	for(int i=1;i<=n;i++){
		f[i]=1ll*(1+qzh[p[i]])%mod*qpow(((1-ll[p[i]])%mod+mod)%mod,mod-2)%mod;
		for(int j=0;j<=99;j++) qzh[j]=1ll*ll[j]*((1ll*qzh[j]+1ll*ll[j]*f[i]%mod)%mod)%mod;
	}
	int ans=0;
	for(int i=1;i<=n;i++) ans=(ans+f[i])%mod;
	cout<<ans;
	return 0;
}
posted @ 2025-08-24 22:09  _dlwlrma  阅读(3)  评论(0)    收藏  举报