Public Easy Round #2 E 2048
\(f\):长度为 \(i\) 的栈,栈底先填一个 \(j\) 的期望得分
- case 1 : 直接把 \(j\) 升级。
- case 2 : 下一个填比 \(j\) 大的,那我 \(j\) 就可以直接摆烂停止考虑,直接归约。
- case 3 : 下一个比 \(j\) 小,那就要小心控制使得 \(j\) 不被升级。
因此得到另外的 \(dp\) 定义:
将 \(j\) 升级需要再造一个 \(j\) 在后面,因此
\(g\):长度为 \(i\) 的栈,一波操作后得到单个 \(j\) 的 概率/期望得分。
- case 1 : 人品爆发直接随一个出来
- case 2 : 从 \(j - 1\) 合并上来,要先造一个 \(j - 1\) 再造一个 \(j - 1\)。
显然对于 \(g\) 的转移我们不用维护更多东西。
考虑 \(f\) 的其他转移。
怎样使得 \(j\) 再之后不被升级?
首先因为填的数比他小,因此这个数无论怎样被合并都不应该超越他,否则会被升级。
\(h\):长度为 \(i\) 的栈,乱填一波,最后开头是个 \(j\) 的期望得分(好像不需要概率信息)。
case :
要造一个数 \(j\) 出来,那就先合一个出来,然后让之后的东西不能给他升级(可能直接出比他大的,也可能比他小的合不上去)。
然后剩下部分的期望得分,一个是填出来开头比他小(归约至 \(j\)),另一个是填出来比他大(当且仅当开局就大,归约至 \(f\))。
参考实现
int main() {
read(n),read(m);
int Tot = 0;
for (int i = 1; i <= m; ++i) read(a[i]) , Tot += a[i];
Tot = Qpow(Tot , mod - 2);
Power[0] = 1;
for (int i = 1; i <= m; ++i) p[i] = Mul(Tot , a[i]) , g[0][1][i] = p[i];
for (int i = 1; i <= n + m; ++i) Power[i] = Mul(Power[i - 1] , 2);
for (int i = 2; i <= n; ++i) {
for (int j = 1; j <= n + m; ++j) {
g[0][i][j] = Add(p[j] , Mul(g[0][i][j - 1] , g[0][i - 1][j - 1]));
g[1][i][j] = Add(Mul(Power[j] , Mul(g[0][i][j - 1] , g[0][i - 1][j - 1])) , Add(Mul(g[0][i - 1][j - 1] , g[1][i][j - 1]) , Mul(g[1][i - 1][j - 1] , g[0][i][j - 1])));
h[i][j] = Add(Mul(g[1][i][j] , Sub(1 , g[0][i - 1][j])) , Mul(g[0][i][j] , Add(Ph[i - 1][j - 1] , Sf[i - 1][j + 1])));
Ph[i][j] = Add(Ph[i][j - 1] , h[i][j]);
}
for (int j = n + m; j >= 1; --j) {
f[i][j] = Add(Add(Mul(g[0][i - 1][j] , Add(f[i][j + 1] , Power[j + 1])) , g[1][i - 1][j]) , Add(Ph[i - 1][j - 1] , Sf[i - 1][j + 1]));
Sf[i][j] = Add(Sf[i][j + 1] , Mul(f[i][j] , p[j]));
}
}
write(Mul(Sf[n][1] , ((mod + 1) >> 1)));
return 0;
}