好妙的一个题…

我们设 \(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. Codeforces_739_B

    http://codeforces.com/problemset/problem/739/B dfs,记录距离前缀和,每次找到离最近的不符合的点. #include<iostream> # ...

  2. 【Java并发工具类】Semaphore

    前言 1965年,荷兰计算机科学家Dijkstra提出的信号量机制成为一种高效的进程同步机制.这之后的15年,信号量一直都是并发编程领域的终结者.1980年,管程被提出,成为继信号量之后的在并发编程领 ...

  3. symfony传参,接收参数,twig方法记录

    呜呜呜,很烦,让我自己完成一个在线学习系统后端,和前端整合一下,我把接口参数搞了半天(学习symfony太久远),记录一下屈辱历史,以后注意,不然上线了一堆bug,很烦 下面是几种返回的数据的格式 1 ...

  4. Spark中的RDD和DataFrame

    什么是DataFrame 在Spark中,DataFrame是一种以RDD为基础的分布式数据集,类似于传统数据库中的二维表格. RDD和DataFrame的区别 DataFrame与RDD的主要区别在 ...

  5. 基于python的密码字典生成器

    最近在网上看到一些作品,然后对其进行了一些完善.只是用于学习,不要去干坏事哦.程序来源于网络,我只是做了一些优化. #!/usr/bin/python# -*- coding:utf-8 -*-# @ ...

  6. ubuntu 配置网卡,DNS, iptables

    # 配置静态ip地址 root@simon:~# vim /etc/network/interfaces auto enp4s0 iface enp4s0 inet static address 19 ...

  7. HDU 2018 DP

    A - 母牛的故事 Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit St ...

  8. 【全集】大数据Linux基础

    课程介绍 本课程是由猎豹移动大数据架构师,根据公司大数据平台的运维情况,精心设计和打磨的大数据必备Linux课程.通过本课程的学习大数据新手能够少走弯路,快速掌握Linux常用命令及Shell编程,为 ...

  9. 学习MVC框架,处理分页和删除分页转跳的问题

    第一次写博客,文采不好请多见谅,这里主要是写一下,自己是如何处理分页问题,我想初学者也遇到过这个问题. 分页的情况下,编辑信息有返回和编辑2个按钮,操作后都是应该返回原分页界面,使用TempData把 ...

  10. 【WPF学习】第四十九章 基本动画

    在前一章已经学习过WPF动画的第一条规则——每个动画依赖于一个依赖项属性.然而,还有另一个限制.为了实现属性的动态化(换句话说,使用基于时间的方式改变属性的值),需要有支持相应数据类型的动画类.例如, ...