LOJ2537 PKUWC2018 Minimax 树形DP、线段树合并
题意:自己去看
首先可以知道,每一个点都有几率被选到,所以$i$与$V_i$的关系是确定了的。
所以我们只需要考虑每一个值的取到的概率。
很容易设计出一个$DP$:设$f_{i,j}$为在第$i$个点取到权值第$j$小的点的概率,转移就是$f_{i,j}=f_{lson,j} \times (\sum \limits _{k<i} f_{rson,k} \times p_x + \sum \limits _{k > i} f_{rson,k} \times (1 - p_x))$($lson$和$rson$之间可以交换),显然是可以前缀和优化的
当然前缀和优化也不够,$O(n^2)$只能过$40pts$。考虑优化。发现在合并的时候$lson$与$rson$之间的元素是互不冲突的,所以可以考虑线段树合并,每一次合并的时候把两边的贡献记录下来,在线段树上打标记即可。
#include<bits/stdc++.h>
#define ld long double
#define int long long
#define mid ((l + r) >> 1)
#define lch Tree[now].ch[0]
#define rch Tree[now].ch[1]
//This code is written by Itst
using namespace std; inline int read(){
int a = ;
bool f = ;
char c = getchar();
while(c != EOF && !isdigit(c)){
if(c == '-')
f = ;
c = getchar();
}
while(c != EOF && isdigit(c)){
a = (a << ) + (a << ) + (c ^ '');
c = getchar();
}
return f ? -a : a;
} const int MAXN = , MOD = ;
struct node{
int mark , sum , ch[];
}Tree[MAXN * ];
int pri[MAXN] , root[MAXN] , lsh[MAXN] , ch[MAXN][] , cnt , N , cntNode , ny; inline void pushup(int now){
Tree[now].sum = (Tree[lch].sum + Tree[rch].sum) % MOD;
} inline void pushdown(int now){
if(Tree[now].mark != ){
Tree[lch].sum = Tree[lch].sum * Tree[now].mark % MOD;
Tree[rch].sum = Tree[rch].sum * Tree[now].mark % MOD;
Tree[lch].mark = Tree[lch].mark * Tree[now].mark % MOD;
Tree[rch].mark = Tree[rch].mark * Tree[now].mark % MOD;
Tree[now].mark = ;
}
} void insert(int& now , int l , int r , int tar){
if(!now){
now = ++cntNode;
Tree[now].mark = ;
}
if(l == r){
Tree[now].sum = ;
return;
}
pushdown(now);
if(mid >= tar)
insert(lch , l , mid , tar);
else
insert(rch , mid + , r , tar);
pushup(now);
} int mer(int p , int q , int markp , int markq , int pri){
if(!(p + q))
return ;
if(!p){
Tree[q].mark = Tree[q].mark * markq % MOD;
Tree[q].sum = Tree[q].sum * markq % MOD;
return q;
}
if(!q){
Tree[p].mark = Tree[p].mark * markp % MOD;
Tree[p].sum = Tree[p].sum * markp % MOD;
return p;
}
pushdown(p);
pushdown(q);
int m1 = Tree[Tree[q].ch[]].sum , n1 = Tree[Tree[p].ch[]].sum , m2 = Tree[Tree[q].ch[]].sum , n2 = Tree[Tree[p].ch[]].sum;
Tree[p].ch[] = mer(Tree[p].ch[] , Tree[q].ch[] , (markp + m1 * ( - pri) % MOD * ny) % MOD , (markq + n1 * ( - pri) % MOD * ny) % MOD , pri);
Tree[p].ch[] = mer(Tree[p].ch[] , Tree[q].ch[] , (markp + m2 * pri % MOD * ny) % MOD , (markq + n2 * pri % MOD * ny) % MOD , pri);
pushup(p);
return p;
} int getAns(int now , int l , int r){
if(l == r)
return l * lsh[l] % MOD * Tree[now].sum % MOD * Tree[now].sum % MOD;
else{
pushdown(now);
return (getAns(lch , l , mid) + getAns(rch , mid + , r)) % MOD;
}
} void dfs(int now){
if(!ch[now][])
insert(root[now] , , cnt , pri[now]);
else
if(!ch[now][]){
dfs(ch[now][]);
root[now] = root[ch[now][]];
}
else{
dfs(ch[now][]);
dfs(ch[now][]);
root[now] = mer(root[ch[now][]] , root[ch[now][]] , , , pri[now]);
}
} inline int poww(long long a , int b){
int times = ;
while(b){
if(b & )
times = times * a % MOD;
a = a * a % MOD;
b >>= ;
}
return times;
} signed main(){
#ifndef ONLINE_JUDGE
freopen("2537.in" , "r" , stdin);
//freopen("2537.out" , "w" , stdout);
#endif
N = read();
for(int i = ; i <= N ; ++i){
int a = read();
if(!ch[a][])
ch[a][] = i;
else
ch[a][] = i;
}
ny = poww( , MOD - );
for(int i = ; i <= N ; ++i){
pri[i] = read();
if(!ch[i][])
lsh[++cnt] = pri[i];
}
sort(lsh + , lsh + cnt + );
for(int i = ; i <= N ; ++i)
if(!ch[i][])
pri[i] = lower_bound(lsh + , lsh + cnt + , pri[i]) - lsh;
dfs();
cout << getAns(root[] , , cnt);
return ;
}
LOJ2537 PKUWC2018 Minimax 树形DP、线段树合并的更多相关文章
- [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\)的概率是子节点点权较小值. ...
- LOJ2537. 「PKUWC2018」Minimax【概率DP+线段树合并】
LINK 思路 首先暴力\(n^2\)是很好想的,就是把当前节点概率按照权值大小做前缀和和后缀和然后对于每一个值直接在另一个子树里面算出贡献和就可以了,注意乘上选最大的概率是小于当前权值的部分,选最小 ...
- loj2537 「PKUWC2018」Minimax 【概率 + 线段树合并】
题目链接 loj2537 题解 观察题目的式子似乎没有什么意义,我们考虑计算出每一种权值的概率 先离散化一下权值 显然可以设一个\(dp\),设\(f[i][j]\)表示\(i\)节点权值为\(j\) ...
- 【pkuwc2018】 【loj2537】 Minmax DP+线段树合并
今年年初的时候参加了PKUWC,结果当时这一题想了快$2h$都没有想出来.... 哇我太菜啦.... 昨天突然去搜了下哪里有题,发现$loj$上有于是就去做了下. 结果第一题我5分钟就把所有细节都想好 ...
- BZOJ.5461.[PKUWC2018]Minimax(DP 线段树合并)
BZOJ LOJ 令\(f[i][j]\)表示以\(i\)为根的子树,权值\(j\)作为根节点的概率. 设\(i\)的两棵子树分别为\(x,y\),记\(p_a\)表示\(f[x][a]\),\(p_ ...
- [PKUWC2018]Minimax [dp,线段树合并]
好妙的一个题- 我们设 \(f_{i,j}\) 为 \(i\) 节点出现 \(j\) 的概率 设 \(l = ch[i][0] , r = ch[i][1]\) 即左儿子右儿子 设 \(m\) 为叶子 ...
- P6847-[CEOI2019]Magic Tree【dp,线段树合并】
正题 题目链接:https://www.luogu.com.cn/problem/P6847 题目大意 \(n\)个点的一棵树上,每个时刻可以割掉一些边,一些节点上有果实表示如果在\(d_i\)时刻这 ...
- POJ 3162 Walking Race 树形DP+线段树
给出一棵树,编号为1~n,给出数m 漂亮mm连续n天锻炼身体,每天会以节点i为起点,走到离i最远距离的节点 走了n天之后,mm想到知道自己这n天的锻炼效果 于是mm把这n天每一天走的距离记录在一起,成 ...
随机推荐
- ios开发GCD(2)-dispatch_semaphore_t信号量计数器
思考:现在有多个线程异步执行,我们想要同时最多只能执行2个或n个,该怎么办? dispatch_semaphore_t 看代码解析: NSLog(@"开始"); dispatch_ ...
- echart参数设置——曲线图
{ title: { text: '请求返回码分布', subtext: '实时数据' }, tooltip: { trigger: 'axis', position: function (point ...
- cc1plus.exe: error: unrecognized command line option "-fno-keep-inline-dllexport "
在Windows环境下的控制台上,通过qmake指令编译Qt程序时,出现 cc1plus.exe: error: unrecognized command line option "-fno ...
- JDK动态代理源码解析
动态代理.静态代理优缺点 关于JDK的动态代理,最为人熟知的可能要数Spring AOP的实现,默认情况下,Spring AOP的实现对于接口来说就是使用的JDK的动态代理来实现的,而对于类的 ...
- bootstrap-datepicker default value
$('.selectDate').datepicker({ format : "yyyy/mm/dd", autoclose : true, startDate : new Dat ...
- Vue框架的两种使用方式
1.单页面应用:使用Vue CLI工具生成脚手架,这是最常见的使用方式,简单用模板生成一个HelloWorld Demo,可以学习Vue的SPA项目结构 2.传统多页面应用:通过script引入Vue ...
- 怎么配置 Oracle 侦听器来使用SQL操作ST_Geometry
关于这个内容,其实从ArcSDE9.2推出ST_Geometry就让用户感到很有吸引力,而且特别是在ArcSDE9.3之后,用户使用SQL操作ST_geometry越来越多,但是在配置Oracle监听 ...
- GUI_菜单练习
package com.mywindow.test; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; ...
- yii2 下拉菜单
model public static function getCatlist(){ $cat = ['0' => '暂无分类']; $res = self::find()->asArra ...
- Java逻辑运算
逻辑运算是在关系运算基础之上的运算,能处理更加复杂的问题 逻辑运算的结果是 true 或 false 一.逻辑运算的种类: 在java的逻辑运算符中,有这么四类&&(短路与).& ...