好妙的一个题…

我们设 \(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. Django面试集锦(1-50)

    目录 1.Django ORM查询中select_related和prefetch_related的区别? 2.only与defer的用法? 3.Django ORM是什么? 4.Django创建项目 ...

  2. asp.net core 3.x 身份验证-1涉及到的概念

    前言 从本篇开始将围绕asp.net core身份验证写个小系列,希望你看完本系列后,脑子里对asp.net core的身份验证原理有个大致印象.至于身份验证是啥?与授权有啥联系?就不介绍了,太啰嗦. ...

  3. shell正则表达式提取数字

    grep 提取数字 grep -Po "\d+\.\d+"

  4. Linux的总结

    Linux学习总结: 1.需要熟练掌握各种命令,这是保证工作效率的基础.经常使用的命令 1)tar 解压 2)pwd查看所在的根目录 3)mkdir touch cp cat... 4)   mv 移 ...

  5. python笔记18(复习)

    今日内容 复习 内容详细 1.Python入门 1.1 环境的搭建 mac系统上搭建python环境. 环境变量的作用:方便在命令行(终端)执行可执行程序,将可执行程序所在的目录添加到环境变量,那么以 ...

  6. Markdown 教程

    Markdown 简介 Markdown 是一种轻量级标记语言,它允许人们使用易读易写的纯文本格式编写文档. Markdown 语言在 2004 由约翰·格鲁伯(英语:John Gruber)创建. ...

  7. vmware14 unlock开启macos选项

    之前搜索了很多资料,用了很多Unlock都失败了,最后重新卸载vmware重新安装后,关闭应用竟然可以了 工具在微信公众号菜菜电脑已保存到百度网盘

  8. SpringBoot使用ELK日志收集ELASTIC (ELK) STACK

    1:资源 # 文档向导 # logstash https://www.elastic.co/guide/en/logstash/current/index.html #kibana https://w ...

  9. 13.深度学习(词嵌入)与自然语言处理--HanLP实现

    笔记转载于GitHub项目:https://github.com/NLP-LOVE/Introduction-NLP 13. 深度学习与自然语言处理 13.1 传统方法的局限 前面已经讲过了隐马尔可夫 ...

  10. vue路由核心要点(vue-router)

    目录 目录 1.vue-router 是什么? 2.如何使用v-router? 3.vue-router跳转和传参 4.vue-router实现的原理 两种模式 5.vue-router 有哪几种导航 ...