好妙的一个题…

我们设 \(f_{i,j}\) 为 \(i\) 节点出现 \(j\) 的概率

设 \(l = ch[i][0] , r = ch[i][1]\)

即左儿子右儿子

设 \(m\) 为叶子结点的个数

显然,\(i\) 出现 \(j\) 的概率为

\[f_{i,j} = f_{l,j} * (p_i \sum_{k=1}^{j-1}f_{r,k} + (1-p_i)\sum_{k=j+1}^{m}f_{r,k}) + f_{r,j} * (p_i \sum_{k=1}^{j-1}f_{l,k} + (1-p_i)\sum_{k=j+1}^{m}f_{l,k})
\]

不难发现,这个柿子有关前缀和和后缀和,可以用线段树合并的操作来进行转移,从下到上转移,求出根节点的概率就好了…

#include <cstdio>
#include <algorithm> int read() {
int x = 0;
char c = 0;
while (c < 48) c = getchar();
while (c > 47) x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return x;
} const int mod = 998244353;
int qpow(int x, int y) {
int ans = 1;
for (; y; y >>= 1, x = 1ll * x * x % mod)
if (y & 1) ans = 1ll * ans * x % mod;
return ans;
} int n;
const int maxn = 3e5 + 10;
int ch[maxn][2], fa[maxn], cnt[maxn], val[maxn], tmp[maxn], qwq = 0, s[maxn];
int rt[maxn], ls[maxn << 5], rs[maxn << 5], sum[maxn << 5], mul[maxn << 5];
int ans = 0, tot = 0; void pushup(int rt) { sum[rt] = (sum[ls[rt]] +sum[rs[rt]]) % mod; }
void pushmul(int rt, int v) {
if (!rt) return;
sum[rt] = 1ll * sum[rt] * v % mod;
mul[rt] = 1ll * mul[rt] * v % mod;
} void pushd(int rt) {
if (mul[rt] == 1) return;
if (ls[rt]) pushmul(ls[rt], mul[rt]);
if (rs[rt]) pushmul(rs[rt], mul[rt]);
mul[rt] = 1;
} int newnode() {
int x = ++ tot;
ls[x] = rs[x] = sum[x] = 0, mul[x] = 1 ;
return x ;
}
void upd(int& p, int l, int r, int x, int v) {
if (!p) p = newnode() ;
if (l == r) {
sum[p] = v;
return;
}
pushd(p);
int mid = l + r >> 1;
(x <= mid) ? upd(ls[p], l, mid, x, v) : upd(rs[p], mid + 1, r, x, v);
pushup(p);
} int merge(int x, int y, int l, int r, int xmul, int ymul, int v) {
if (!x && !y) return 0;
if (!x) {
pushmul(y, ymul);
return y;
}
if (!y) {
pushmul(x, xmul);
return x;
}
pushd(x), pushd(y);
int mid = l + r >> 1;
int lsx = sum[ls[x]], lsy = sum[ls[y]], rsx = sum[rs[x]], rsy = sum[rs[y]];
ls[x] = merge(ls[x], ls[y], l, mid, (xmul + 1ll * rsy % mod * (1 - v + mod)) % mod,
(ymul + 1ll * rsx % mod * (1 - v + mod)) % mod, v);
rs[x] = merge(rs[x], rs[y], mid + 1, r, (xmul + 1ll * lsy % mod * v) % mod,
(ymul + 1ll * lsx % mod * v) % mod, v);
pushup(x);
return x;
} void out(int x, int l, int r) {
if (!x) return;
if (l == r) {
s[l] = sum[x];
return;
}
int mid = l + r >> 1;
pushd(x);
out(ls[x], l, mid);
out(rs[x], mid + 1, r);
} void dfs(int u) {
if (!cnt[u]) upd(rt[u], 1, qwq, val[u], 1);
if (cnt[u] == 1) dfs(ch[u][0]), rt[u] = rt[ch[u][0]] ;
if (cnt[u] == 2) dfs(ch[u][0]), dfs(ch[u][1]), rt[u] = merge(rt[ch[u][0]], rt[ch[u][1]] ,1 , qwq , 0 , 0 , val[u]);
} int main() {
n = read();
for (int i = 1; i <= n; i++) fa[i] = read();
for (int i = 1; i <= n; i++)
if (fa[i]) ch[fa[i]][cnt[fa[i]]++] = i;
for (int i = 1; i <= n; i++) val[i] = read();
for (int i = 1; i <= n; i++) {
if (cnt[i]) {
val[i] = 1ll * val[i] * qpow(10000, mod - 2) % mod;
} else {
tmp[++qwq] = val[i];
}
}
std ::sort(tmp + 1, tmp + qwq + 1);
for (int i = 1; i <= n; i++)
if (!cnt[i]) val[i] = std ::lower_bound(tmp + 1, tmp + qwq + 1, val[i]) - tmp;
dfs(1);
out(rt[1], 1, qwq);
for (int i = 1; i <= qwq; i++) ans = (ans + 1ll * i * tmp[i] % mod * s[i] % mod * s[i]) % mod;
printf("%d\n", ans);
return 0;
}

[PKUWC2018]Minimax [dp,线段树合并]的更多相关文章

  1. BZOJ.5461.[PKUWC2018]Minimax(DP 线段树合并)

    BZOJ LOJ 令\(f[i][j]\)表示以\(i\)为根的子树,权值\(j\)作为根节点的概率. 设\(i\)的两棵子树分别为\(x,y\),记\(p_a\)表示\(f[x][a]\),\(p_ ...

  2. [BZOJ5461][LOJ#2537[PKUWC2018]Minimax(概率DP+线段树合并)

    还是没有弄清楚线段树合并的时间复杂度是怎么保证的,就当是$O(m\log n)$吧. 这题有一个显然的DP,dp[i][j]表示节点i的值为j的概率,转移时维护前缀后缀和,将4项加起来就好了. 这个感 ...

  3. LOJ2537. 「PKUWC2018」Minimax【概率DP+线段树合并】

    LINK 思路 首先暴力\(n^2\)是很好想的,就是把当前节点概率按照权值大小做前缀和和后缀和然后对于每一个值直接在另一个子树里面算出贡献和就可以了,注意乘上选最大的概率是小于当前权值的部分,选最小 ...

  4. 【洛谷5298】[PKUWC2018] Minimax(树形DP+线段树合并)

    点此看题面 大致题意: 有一棵树,给出每个叶节点的点权(互不相同),非叶节点\(x\)至多有两个子节点,且其点权有\(p_x\)的概率是子节点点权较大值,有\(1-p_x\)的概率是子节点点权较小值. ...

  5. 【pkuwc2018】 【loj2537】 Minmax DP+线段树合并

    今年年初的时候参加了PKUWC,结果当时这一题想了快$2h$都没有想出来.... 哇我太菜啦.... 昨天突然去搜了下哪里有题,发现$loj$上有于是就去做了下. 结果第一题我5分钟就把所有细节都想好 ...

  6. P6847-[CEOI2019]Magic Tree【dp,线段树合并】

    正题 题目链接:https://www.luogu.com.cn/problem/P6847 题目大意 \(n\)个点的一棵树上,每个时刻可以割掉一些边,一些节点上有果实表示如果在\(d_i\)时刻这 ...

  7. 洛谷P4577 [FJOI2018]领导集团问题(dp 线段树合并)

    题意 题目链接 Sol 首先不难想到一个dp,设\(f[i][j]\)表示\(i\)的子树内选择的最小值至少为\(j\)的最大个数 转移的时候维护一个后缀\(mx\)然后直接加 因为后缀max是单调不 ...

  8. BZOJ 5469: [FJOI2018]领导集团问题 dp+线段树合并

    在 dp 问题中,如果发现可以用后缀最大值来进行转移的话可以考虑去查分这个后缀最大值. 这样的话可以用差分的方式来方便地进行维护 ~ #include <bits/stdc++.h> #d ...

  9. LOJ2537 PKUWC2018 Minimax 树形DP、线段树合并

    传送门 题意:自己去看 首先可以知道,每一个点都有几率被选到,所以$i$与$V_i$的关系是确定了的. 所以我们只需要考虑每一个值的取到的概率. 很容易设计出一个$DP$:设$f_{i,j}$为在第$ ...

随机推荐

  1. 基于Arduino开发的智能蓝牙小车

    基于Arduino的智能蓝牙小车 材料准备: Arduino开发板一块.四驱小车底板及相关配件一套.L298N驱动模块一个.HC-05/06蓝牙模块一块,九伏电源一块(用于主板供电).12V锂电池一块 ...

  2. Java并发编程(二):volatile关键字

    volatile是Java虚拟机提供的轻量级的同步机制.volatile关键字有如下两个作用,一句话概括就是内存可见性和禁止重排序. 1)保证被volatile修饰的共享变量对所有线程总是可见的,也就 ...

  3. 文件类练习题(FileInputStream类)

    应用FileInputStream类,编写应用程序,从磁盘上读取一个Java程序,并将源程序代码显示在屏幕上.(被读取的文件路径为:d:/myjava/Hello.java 内容自己决定): pack ...

  4. 搭建一个V 2ray的方法

    VPS构建VPN教程 (由于博客限制有些敏感词 V 2ray中间会打空格或者(删掉我)图片中的敏感词进行了马赛克处理) 关于自建VPN翻墙教程,此处是利用V 2 ray的一个VPS搭建VPN教程.便于 ...

  5. HDU4195 Regular Convex Polygon (正多边形、外接圆)

    题意: 给你正n边形上的三个点,问n最少为多少 思路: 三个点在多边形上,所以三个点的外接圆就是这个正多边形的外接圆,余弦定理求出每个角的弧度值,即该角所对边的圆周角,该边对应的圆心角为圆心角的二倍. ...

  6. layui父子页面方法互调

    父级页面调用子页面方法 layer.open({ type: 2, content: 'test/iframe.html', success: function(layero, index){ var ...

  7. Go语言实现:【剑指offer】二叉树中和为某一值的路径

    该题目来源于牛客网<剑指offer>专题. 输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径.路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路 ...

  8. Linux 系统监控工具 atop

    系统监控是运维工作中重要的一环,本文以 atop 工具为例来介绍系统的重要监控项. atop可以使用yum或apt包管理器进行安装.atop man page 中详细说明了 atop 中各监控项含义及 ...

  9. 【算法】混合流体模拟demo

    展示一个流体模拟算法的实现 地址:http://www.iqiyi.com/w_19rzs1anol.html 采用C++编写,Blender渲染. 截图 参考文献 REN, B., LI, C., ...

  10. Classmethod and Staticmethod - Python 类方法 和 静态方法

    classmethod and staticmethod classmethod 的是一个参数是类对象 cls (本类,或者子类), 而不是实例对象 instance (普通方法). classmet ...