Luogu5298 [PKUWC2018]Minimax
太久没写博客了,过来水一发。
题目链接:洛谷
首先我们想到,考虑每个叶节点的权值为根节点权值的概率。首先要将叶节点权值离散化。
假设现在是$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的更多相关文章
- BZOJ5461: [PKUWC2018]Minimax
BZOJ5461: [PKUWC2018]Minimax https://lydsy.com/JudgeOnline/problem.php?id=5461 分析: 写出\(dp\)式子:$ f[x] ...
- 题解-PKUWC2018 Minimax
Problem loj2537 Solution pkuwc2018最水的一题,要死要活调了一个多小时(1h59min) 我写这题不是因为它有多好,而是为了保持pkuwc2018的队形,与这题类似的有 ...
- [PKUWC2018] Minimax
Description 给定一棵 \(n\) 个节点的树,每个节点最多有两个子节点. 如果 \(x\) 是叶子,则给定 \(x\) 的权值:否则,它的权值有 \(p_x\) 的概率是它子节点中权值的较 ...
- BZOJ.5461.[PKUWC2018]Minimax(DP 线段树合并)
BZOJ LOJ 令\(f[i][j]\)表示以\(i\)为根的子树,权值\(j\)作为根节点的概率. 设\(i\)的两棵子树分别为\(x,y\),记\(p_a\)表示\(f[x][a]\),\(p_ ...
- LOJ2537 PKUWC2018 Minimax 树形DP、线段树合并
传送门 题意:自己去看 首先可以知道,每一个点都有几率被选到,所以$i$与$V_i$的关系是确定了的. 所以我们只需要考虑每一个值的取到的概率. 很容易设计出一个$DP$:设$f_{i,j}$为在第$ ...
- LOJ2537:[PKUWC2018]Minimax——题解
https://loj.ac/problem/2537 参考了本题在网上能找到的为数不多的题解. 以及我眼睛瞎没看到需要离散化,还有不开longlong见祖宗. ——————————————————— ...
- [BZOJ5461][LOJ#2537[PKUWC2018]Minimax(概率DP+线段树合并)
还是没有弄清楚线段树合并的时间复杂度是怎么保证的,就当是$O(m\log n)$吧. 这题有一个显然的DP,dp[i][j]表示节点i的值为j的概率,转移时维护前缀后缀和,将4项加起来就好了. 这个感 ...
- 【洛谷5298】[PKUWC2018] Minimax(树形DP+线段树合并)
点此看题面 大致题意: 有一棵树,给出每个叶节点的点权(互不相同),非叶节点\(x\)至多有两个子节点,且其点权有\(p_x\)的概率是子节点点权较大值,有\(1-p_x\)的概率是子节点点权较小值. ...
- Luogu P5298 [PKUWC2018]Minimax
好劲的题目啊,根本没往线段树合并方面去想啊 首先每种权值都有可能出现,因此我们先排个序然后一个一个求概率 由于此时数的值域变成\([1,m]\)(离散以后),我们可以设一个DP:\(f_{x,i}\) ...
随机推荐
- java代码检出打包
这里先提下前提,就是有个维护的(可能有二期的一个项目),后端是Java,由于很久都不做Java,剩下的只是不多了.之前做的Java容器要么是tomcat,要么接触过新的spring cloud.从来没 ...
- [Es6]原生Promise的使用方法
参考:https://www.cnblogs.com/imwtr/p/5916793.html 1.new Promise(func) 通过实例化构造函数成一个promise对象,构造函数中有个函数参 ...
- SQL Server 2017 左补齐
DECLARE @NUM CHAR(3)='7 'SELECT RIGHT('0000000'+CONVERT(VARCHAR(50),1+ RTRIM(@NUM)),7)
- krpano 全景学习
krpano 切片工具下载 https://krpano.com/tools/ krpano 案例使用 https://krpano.com/examples/usage/#top krpano 是 ...
- C#强制回收垃圾
[DllImport("psapi.dll")] private static extern int EmptyWorkingSet(int hProcess); public v ...
- Java 面向对象(一)面向对象思想
一.面向对象思想 1.概述 Java语言是一种面向对象的程序设计语言,而面向对象思想是一种程序设计思想,我们在面向对象思想的指引下,使用Java语言去设计.开发计算机程序. 这里的对象泛指现实中一切事 ...
- iOS 如何判断一个点在圆、方框、三角形区域内?
如何判断一个点是不是在方框(CGRect).圆(Circle).三角形(Triangle)内呢? 1.方框 //苹果官方方法可以判断 + (BOOL)point:(CGPoint)point inSq ...
- 如何用HAProxy+Nginx实现负载均衡
一.什么是HAProxy HAProxy提供高可用性.负载均衡以及基于TCP和HTTP应用的代理,支持虚拟主机,它是免费.快速并且可靠的一种解决方案.HAProxy特别适用于那些负载特大的web站点, ...
- 怎么制作电脑系统安装U盘?
现如今U盘安装电脑系统已经是非常普遍的一种方式,这种方式简单好用,能应对大多数情况,受到很多用户的欢迎. 雨后清风U盘启动是一款可将普通U盘制作为系统引导启动工具的软件,其制作的U盘启动盘融合了雨后清 ...
- 11 Windows编程——定时器
周期性的发送WWL_TIMER消息的一个东西,这个周期可以由程序员自己设定.设定周期的数是SetTimer,停止定时器消息发送的函数是:Killximer: 定时器消息的特点: 1.不准确(也就是说, ...