好妙的一个题…

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

    1. int indexOf(int ch):用来检查给定的一个字符在当前字符串中第一次出现的下标位置.这里的下标和数组的下标意思相近,0表示该字符串的第1个字符,以此类推.当该字符串中并不     ...

  2. BJUT数字图像处理作业

    一. n的正方形图像,用FFT算法从空域变换到频域,并用频域图像的模来进行显示. 2) 使图像能量中心,对应到几何中心,并用频域图像的模来进行显示. 3)将频域图象,通过FFT逆变换到空域,并显示. ...

  3. WeChall_Training: Get Sourced (Training)

    The solution is hidden in this page Use View Sourcecode to get it 解题: 网页源码,最后一行 <!-- You are look ...

  4. Python中zip()函数的解释和可视化

    zip()的作用 先看一下语法: zip(iter1 [,iter2 [...]]) -> zip object Python的内置help()模块提供了一个简短但又有些令人困惑的解释: 返回一 ...

  5. python代码迷之错误(ModuleNotFoundError: No module named 'caffe.proto')

    1.pandas.read_csv book[n]下的print(n) 总图片数是少一张的,print(n)发现也是少了一个序号 仔细查找后发现缺少99号,即最后一张图片没有被读取.print(m)时 ...

  6. Ops:jar包启动关闭脚本

    简介 公司开发架构为java语言的rpc dubbo架构,将功能分解为各个模块,模块较多,发布到环境上的应用为编译后的jar包和配置文件,以及启动关闭jar包的shell脚本.之前经常会出现进程启动不 ...

  7. Go语言实现:【剑指offer】数组中重复的数字

    该题目来源于牛客网<剑指offer>专题. 在一个长度为n的数组里的所有数字都在0到n-1的范围内.数组中某些数字是重复的,但不知道有几个数字是重复的.也不知道每个数字重复几次.请找出数组 ...

  8. 安装symfony3.4的坑,也是PHP7.3的经典坑之解决办法

    对于刚入手symfony3.4的同学,肯定会发现,安装symfony后部署后看到的往往不是hello world,也不是symfony的欢迎页面,而是给你一个下马威,唉,给你来个bug开开胃. 当然这 ...

  9. 在Windows系统中安装Redis和php_redis扩展

    安装Redis (1)下载redis压缩包,git下载地址https://github.com/MSOpenTech/redis/releases 解压文件夹,在文件夹中运行cmd命令: 输入:   ...

  10. C++ 常用编程--Swap函数有几种写法?

    C++ 常用编程--Swap函数有几种写法? 在说C++模板的方法前,我们先想想C语言里面是怎么做交换的. 举个例子,要将两个int数值交换,是不是想到下面的代码: void swap(int& ...