太久没写博客了,过来水一发。

题目链接:洛谷


首先我们想到,考虑每个叶节点的权值为根节点权值的概率。首先要将叶节点权值离散化。

假设现在是$x$节点,令$f_i,g_i$分别表示左/右节点的权值$=i$的概率。

若$w_x$来自于左儿子,则

$$P(w_x=i)=f_i*(p_x*\sum_{j=1}^{i-1}g_j+(1-p)*\sum_{j=i+1}^mg_j)$$

右儿子也是一样的。

所以在转移的时候需要顺便维护$f,g$的前/后缀和。

但是我们发现这样直接跑是$O(n^2)$的,肯定不行,但是每个节点的所有dp值都只依赖于两个儿子,而且区间乘法是可以使用lazy_tag的,所以可以使用线段树合并

(等会儿,好像之前并没有写过。。。)

线段树合并就是对于值域线段树,合并的时候如果两棵树都有这个节点,那么就递归下去,否则直接按照上面的式子转移。

$f,g$的前/后缀和也可以放在参数里面顺便维护了。

 #include<bits/stdc++.h>
#define Rint register int
using namespace std;
typedef long long LL;
const int N = , mod = , inv = ;
int n, v[N], tot, p[N], fa[N], head[N], to[N], nxt[N];
inline void add(int a, int b){
static int cnt = ;
to[++ cnt] = b; nxt[cnt] = head[a]; head[a] = cnt;
}
int root[N], ls[N << ], rs[N << ], seg[N << ], tag[N << ], cnt, ans;
inline void pushdown(int x){
if(x && tag[x] != ){
if(ls[x]){
seg[ls[x]] = (LL) seg[ls[x]] * tag[x] % mod;
tag[ls[x]] = (LL) tag[ls[x]] * tag[x] % mod;
}
if(rs[x]){
seg[rs[x]] = (LL) seg[rs[x]] * tag[x] % mod;
tag[rs[x]] = (LL) tag[rs[x]] * tag[x] % mod;
}
tag[x] = ;
}
}
inline void change(int &x, int L, int R, int pos){
if(!x) tag[x = ++ cnt] = ;
pushdown(x);
++ seg[x];
if(seg[x] >= mod) seg[x] = ;
if(L == R) return;
int mid = L + R >> ;
if(pos <= mid) change(ls[x], L, mid, pos);
else change(rs[x], mid + , R, pos);
}
inline int merge(int lx, int rx, int L, int R, int pl, int pr, int sl, int sr, int P){
if(!lx && !rx) return ;
int now = ++ cnt, mid = L + R >> ; tag[now] = ;
pushdown(lx); pushdown(rx);
if(!lx){
int v = ((LL) P * sl + (mod + 1ll - P) * sr) % mod;
seg[now] = (LL) seg[rx] * v % mod;
tag[now] = (LL) tag[rx] * v % mod;
ls[now] = ls[rx]; rs[now] = rs[rx];
return now;
}
if(!rx){
int v = ((LL) P * pl + (mod + 1ll - P) * pr) % mod;
seg[now] = (LL) seg[lx] * v % mod;
tag[now] = (LL) tag[lx] * v % mod;
ls[now] = ls[lx]; rs[now] = rs[lx];
return now;
}
ls[now] = merge(ls[lx], ls[rx], L, mid, pl, (pr + seg[rs[rx]]) % mod, sl, (sr + seg[rs[lx]]) % mod, P);
rs[now] = merge(rs[lx], rs[rx], mid + , R, (pl + seg[ls[rx]]) % mod, pr, (sl + seg[ls[lx]]) % mod, sr, P);
seg[now] = (seg[ls[now]] + seg[rs[now]]) % mod;
return now;
}
inline void getans(int x, int L, int R){
pushdown(x);
if(L == R){
ans = (ans + (LL) seg[x] * seg[x] % mod * v[L] % mod * L % mod) % mod;
return;
}
int mid = L + R >> ;
getans(ls[x], L, mid);
getans(rs[x], mid + , R);
}
inline void dfs(int x){
if(!head[x]){
change(root[x], , n, p[x]);
return;
}
for(Rint i = head[x];i;i = nxt[i]){
dfs(to[i]);
if(!root[x]) root[x] = root[to[i]];
else root[x] = merge(root[x], root[to[i]], , n, , , , , p[x]);
}
}
int main(){
scanf("%d", &n);
for(Rint i = ;i <= n;i ++){
scanf("%d", fa + i);
if(fa[i]) add(fa[i], i);
}
for(Rint i = ;i <= n;i ++){
scanf("%d", p + i);
if(head[i]) p[i] = (LL) p[i] * inv % mod;
else v[++ tot] = p[i];
}
sort(v + , v + tot + );
for(Rint i = ;i <= n;i ++)
if(!head[i]) p[i] = lower_bound(v + , v + tot + , p[i]) - v;
dfs();
getans(root[], , n);
printf("%d", ans);
}

luogu5369

Luogu5298 [PKUWC2018]Minimax的更多相关文章

  1. BZOJ5461: [PKUWC2018]Minimax

    BZOJ5461: [PKUWC2018]Minimax https://lydsy.com/JudgeOnline/problem.php?id=5461 分析: 写出\(dp\)式子:$ f[x] ...

  2. 题解-PKUWC2018 Minimax

    Problem loj2537 Solution pkuwc2018最水的一题,要死要活调了一个多小时(1h59min) 我写这题不是因为它有多好,而是为了保持pkuwc2018的队形,与这题类似的有 ...

  3. [PKUWC2018] Minimax

    Description 给定一棵 \(n\) 个节点的树,每个节点最多有两个子节点. 如果 \(x\) 是叶子,则给定 \(x\) 的权值:否则,它的权值有 \(p_x\) 的概率是它子节点中权值的较 ...

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

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

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

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

  6. LOJ2537:[PKUWC2018]Minimax——题解

    https://loj.ac/problem/2537 参考了本题在网上能找到的为数不多的题解. 以及我眼睛瞎没看到需要离散化,还有不开longlong见祖宗. ——————————————————— ...

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

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

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

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

  9. Luogu P5298 [PKUWC2018]Minimax

    好劲的题目啊,根本没往线段树合并方面去想啊 首先每种权值都有可能出现,因此我们先排个序然后一个一个求概率 由于此时数的值域变成\([1,m]\)(离散以后),我们可以设一个DP:\(f_{x,i}\) ...

随机推荐

  1. CSPS2019游记

    Day1: T1:格雷码?看一眼感觉是结论题,但是没头绪推不出来,硬刚40min想到$\oplus$切了. 但是没写unsigned挂了五分... T2:全场爆切人均50的题,就我一个写挂了35pts ...

  2. NavigatorOnLine.onLine——判断设备是否可以上网

    概述:返回浏览器的联网状态.正常联网(在线)返回true,不正常联网(离线)返回false.一旦浏览器的联网状态发生改变,该属性值也会随之变化. 1.语法 let online = window.na ...

  3. Unable to resolve service for type 'Microsoft.AspNetCore.Http.IHttpContextAccessor'

    An unhandled exception occurred while processing the request. InvalidOperationException: Unable to r ...

  4. Asp.Net Core 2.0 之旅---@Html.Action

    原文:Asp.Net Core 2.0 之旅---@Html.Action 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https: ...

  5. CentOS下 .Net Core 1.0 升级到 3.0 遇到的一个小问题

    之前.net core 1.0的安装方式,不是用yum方式安装的,所以,在用yum安装3.0之后,用dotnet --version还是1.0的版本,想起了之前 做过链接映射dotnet目录,删除之前 ...

  6. php 判断是是否是命令行模式

    /* 判断当前的运行环境是否是cli模式  是:true 不是:false */ function is_cli(){    return preg_match("/cli/i", ...

  7. Android中BroadcastReceiver的使用

    1.Android中广播分为静态注册和动态注册 2.下面是一个简单静态注册的例子 创建一个继承BroadcastReceiver的子类 public class DeviceBootReceiver ...

  8. python之re(正则表达式)

    可以参考以下博客 https://www.cnblogs.com/guyuyun/p/5839881.html https://www.runoob.com/python/python-reg-exp ...

  9. <s:bean>标签的使用

    今天在使用<s:bean>时出了一个问题,感觉有意思的就记录下来吧,以备学习 在使用这个标签的时候需要注意两个事项: 1.<s:bean>的三个属性 id,name,var,在 ...

  10. selenium重定向新窗口

    1.跳转新窗口 # 浏览器跳转新窗口后,selenium绑定新窗口 print('页面跳转后重新绑定selenium.') time.sleep(3) search_window = driver.c ...