P4564

这个 CTSC 的题竟然是我自己想出来的,incredible(


Portal

分为两个问题:求每个结界技能每个攻击对象被命中的概率,和求最终每个敌方单位的期望生命值。先考虑后者。

显然只有锁定技能能够造成伤害。我们设敌方单位 (i) 被命中的锁定技能个数为 (x)。将式子列出来:(mathrm E(max(0,a_i-x)))。这个外面套了一个 (max),不好线性性展开。于是我们考虑通过期望的定义,求出每个取值的概率。显然取值个数是 (mathrm O(v)) 的,其中 (v) 是值域大小。于是我们可以对每个 (i) 搞一个背包:(dp_{i,j}) 表示敌方单位 (i) 在当前时刻生命值为 (j) 的概率,在新加进一个锁定技能的时候更新背包的 DP 数组即可。这样一次更新是 (mathrm O(v)) 的,这一部分就是 (mathrm O(Qv))。最后统计一下答案即可。

然后考虑前一个问题。每个敌方单位被命中,首先要他自己还活着,然后根据活着的总人数来确定他在当前情况下被命中的概率。设他活着的情况下,剩下 (i) 个人活着的概率为 (p_i),那么总概率就是 (sum dfrac 1ip_i)。那么我们考虑求出每个敌方单位所对应的 (p) 数组。

显然有一个大暴力:对于每个人都跑一遍背包((dp0_{j}) 表示当前不算第 (i) 个人的情况下,考虑到当前,有 (j) 个人活着的概率,然后转移用每个人存活的概率,这个可以通过当前 (dp) 数组得到),这样一次背包是 (mathrm O!left(n^2 ight)) 的,总复杂度就是 (mathrm O!left(Cn^3 ight)),再优化掉一个 (n) 就可以了。

我们注意到,每个人所对应的背包,是所有人减去他一个人的。那么能否先算出所有人的,再对于每个人,减去他自己这个人呢?也就是背包是否可逆呢?显然,如果要去掉的人是总背包 DP 过程中的最后一个更新它的人,那显然很好去掉,倒推一下即可。而众所周知,背包是顺序无关的,也就是你可以当作它以任意一个人结尾,那就对于每个人就可以倒推了。

讲一下倒推的方法:设更新前的 DP 数组为 (dp0),更新后为 (dp0'),那我们想要通过 (dp0') 知道 (dp0)。转移显然是

[dp0'_j=(1-alive_i)dp0_j+alive_idp0_{j-1} ]

其中 (alive_i) 是第 (i) 个人(也就是假定的最后一个更新的人)存活的概率。那么

[dp0_j=dfrac{dp0'_j-alive_idp0_{j-1}}{1-alive_i} ]

但是这里还有一个问题:(1-alive_i) 可能等于 (0),也就是 (alive_i=1)。这个需要特判一下。事实上这个简单的一批,不难得到

[dp0_j=dp0'_{j+1} ]

于是总复杂度 (mathrm O!left(Qv+Cn^2 ight))。要注意预处理一些逆元,不然复杂度就多了个 (log)

code

原文地址:https://www.cnblogs.com/ycx-akioi/p/solution-p4564.html