(期望dp)CF1866M Mighty Rock Tower
写在前面
岛屿 是的 这里是岛屿 我们彼此造就的小岛,
不错 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\) 层的期望步数,即为:
2.当石头掉到了非零层,参考上面的求法,能得到类似的式子,只不过要多乘上一项:因为到达第\(i\)层不能掉。所以式子为:
所以综合以上情况式子就为:
由于等式右边也存在\(f[i]\),考虑化简。化简的思路大概就是拆项合并项,将所有求和项拆开合并为同起点同终点。大概流程可以参考这篇。
最终化简后的式子即为:
但是本题过于良心,到这里还不让你过。明显看出到目前为止求解的复杂度为\(O(n^2)\) 无法通过。在题解的指引下观察数据范围,\(p[i]\) 的数据范围竟然只有\([0,99]\),而且上式求和一项具有单调性,故可以在计算\(f\)答案时维护求和式的前缀和,设\(qzh[j]\) 为百分率为该值时的和。很简单地推出滚动优化后的式子:
所以本题就解决了。
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;
}

浙公网安备 33010602011771号