Illublog我多想说再见啊

[HNOI2015]亚瑟王

Illu·2022-05-23 11:14·29 次阅读

[HNOI2015]亚瑟王

(看到题解里面有一篇和我最初想法挺像但好像不是大众解法的题解,有点激动,干脆自己也来写篇题解好了)

Description#

你有 n 张卡片,每张有一组数据 di, pi 表示贡献和发动概率,现在你要玩 r 轮游戏,每轮编号从 1 开始:

  • 如果这张卡片在之前被发动过,直接跳过;

  • 如果当前卡片发动了,产生 di 的贡献并直接开始下一轮;

  • 否则没有发动的话,换到下一张卡片。

求期望获得贡献。

多测。

T444, n220, r132, pi[0, 1], di[0, 103]

Analysis#

注意到题目暗含的意思应该是玩多少轮游戏,就会发动多少次卡片。(具体哪张并不清楚)

根据期望的线性性质,我们可以单独考虑每个点发动的概率 gi ,那么总贡献就是 gidi

所以在可以不考虑第几轮的情况下,我们可以这样设一个 DP 状态: dpi, j 表示前 i 张卡片剩下 j 次没发动的期望值。

Solution#

因为每张卡片发动一次之后就不能再发动了,发动之后的卡片视作概率为 1 ,贡献为 0 的“虚卡片”。

假设第 j 轮发动:

  • 对于之后的轮次,概率为 1 ,可以拆成发动 + 不发动两种情况;

  • 而对于前面的轮次,概率一致,属于同类项。

如此来看的话原先定义的 gi 实际上可以变成另一种定义:i 张卡片至少发动一次的概率

会不会算重呢?假如如果第 j 轮前面还有发动的轮次 k ,这种情况就换合并到第一次在第 k 轮发动的,前面有多个同理,不会的。

那就有 g1=1(1p1)r ,如果另加前 j 轮就把 r 改成 j 就好了。

问题解决了一半。

还有一个问题,我们还是被每轮只能发动一次这个条件限制的死死的:因为前者的发动概率会直接影响后面的卡片发动概率,所以同时要算当前状态的概率以及期望。

我们就可以理解为每张卡片在它后面的每张卡片上叠了 buff 。

(不知道怎么的)猛然间想起期望入门的时候做过的这道题

前者影响后者,那就正难则反!!全部逆推,反正后面的卡片都要被前面影响,该叠 buff 就正常叠就行了。

所以就有很自然的 DP 方程式啦(注意是逆推啦):

dpi, j=dpi+1, j(1pi)j+dpi+1, j1(1(1pi)j)

分别的两个系数就是上文讲的发动或者不发动。

(常熟还小/hanx)

Code#

Code
Copy
/* */ #include using namespace std; typedef long long ll; const int N = 222; int n, r, d[N]; double p[N], f[N][N]; inline int read() { char ch = getchar(); int s = 0, w = 1; while (!isdigit(ch)) {if (ch == '-') w = -1; ch = getchar();} while (isdigit(ch)) {s = (s << 3) + (s << 1) + (ch ^ 48); ch = getchar();} return s * w; } inline double dead() { char ch = getchar(); ll s = 0, w = 1, k = 0; double m = 1; bool is = 0; while (!isdigit(ch)) {if (ch == '-') w = -1; ch = getchar();} while (isdigit(ch) || ch == '.') { if (ch == '.') is = 1; else if (!is) s = (s << 3) + (s << 1) + (ch ^ 48); else k = (k << 3) + (k << 1) + (ch ^ 48), m *= 0.1; ch = getchar(); } return (m * k + s) * w; } inline void mian() { memset(f, 0, sizeof(f)); n = read(); r = read(); for (int i = 1; i <= n; ++i) p[i] = dead(), d[i] = read(); for (int i = n; i >= 1; --i) { double P = 1.0 - p[i]; for (int j = 1; j <= r; ++j) { f[i][j] = f[i + 1][j] * P + (f[i + 1][j - 1] + d[i]) * (1.0 - P); P *= 1.0 - p[i]; } } printf("%.9lf\n", f[1][r]); } int main() { int T = read(); while (T--) mian(); return 0; }

posted @   Illusory_dimes  阅读(29)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
点击右上角即可分享
微信分享提示
目录