【pkuwc2018】 【loj2537】 Minmax DP+线段树合并
今年年初的时候参加了PKUWC,结果当时这一题想了快$2h$都没有想出来....
哇我太菜啦....
昨天突然去搜了下哪里有题,发现$loj$上有于是就去做了下。
结果第一题我5分钟就把所有细节都想好了啊5555....
场上$60pts$消失...
显然,我们可以用$f[i][j]$表示节点$i$值为第$j$大的值的概率。
我们不难列出$dp$式子,$f[i][j]=f[s1][j] \times (s[s2][j-1]\times p+(s[s2][m]-s[s2][j])\times (1-p))$。
其中$s[i][j]=\sum_{k=0}{j} f[i][k]$。$s1$表示可以取到第j大的数的儿子,$s2$表示不能取到第$j$大的数的儿子。
显然,直接转移是$O(n^2)$的(我场上就写了这个)。
考虑如何进行优化。
题目中有一些特别优美的条件,比如说所有的数不会重复,树最多只有两个分叉。
如果某个树只有一个分叉的话,显然直接复制根即可,时间复杂度为$O(1)$。
考虑用线段树优化,考虑如何合并$s1$和$s2$这两棵树。
假设我们当前要合并的区间为$[l,r]$,且$x,y$分别为线段树中$s1,s2$用来表示区间$[l,r]$的节点。
对于该区间,我们用$xs1$来表示$s[s2][l-1]$,用$xs2$来表示$(s[s2][m]-s[s2][r])$。对于$s2$同理(暂且用$ys1,ys2$表示)。
递归的时候,若往线段树的左儿子递归,那么$xs2$就要加上$x$的右儿子的概率和,$ys2$同理。若线段树往右儿子递归,也同理。
然后当递归到$x$或$y$中有一个不存在时,直接用$xs1/2$,$ys1/2$更新即可。
(若$l==r$,则更新方式与上述$dp$式子相同,若$l≠r$,打个标记就行了)
考虑到线段树合并的时间复杂度为$O(n log n)$。所以这种方法是可以通过的。
完结撒花~~
#include<bits/stdc++.h>
#define L long long
#define MOD 998244353
#define M 300005
using namespace std; L pow_mod(L x,L k){
L ans=;
while(k){
if(k&) ans=ans*x%MOD;
x=x*x%MOD; k>>=;
}
return ans;
} int lc[M*]={},rc[M*]={},root[M]={},use=;
L p[M*]={},tag[M*]={},gailv[M]={};
int l[M]={},r[M]={}; void pushdown(int x){
if(tag[x]!=){
if(lc[x]!=) tag[lc[x]]=tag[lc[x]]*tag[x]%MOD,p[lc[x]]=p[lc[x]]*tag[x]%MOD;
if(rc[x]!=) tag[rc[x]]=tag[rc[x]]*tag[x]%MOD,p[rc[x]]=p[rc[x]]*tag[x]%MOD;
tag[x]=;
}
}
void pushup(int x){p[x]=(p[lc[x]]+p[rc[x]])%MOD;} void updata(int &x,int l,int r,int k){
if(!x) {x=++use; p[x]=tag[x]=;}
if(l==r){p[x]=tag[x]=; return;}
int mid=(l+r)>>;
if(k<=mid) updata(lc[x],l,mid,k);
if(mid<k) updata(rc[x],mid+,r,k);
pushup(x);
} L nowp;
int solve(int x,int y,L xs1,L xs2,L ys1,L ys2){
if(x==&&y==) return ;
if(y==){
L upd=(xs1*nowp+xs2*(-nowp+MOD))%MOD;
tag[x]=upd*tag[x]%MOD;
p[x]=upd*p[x]%MOD;
return x;
}
if(x==){
L upd=(ys1*nowp+ys2*(-nowp+MOD))%MOD;
tag[y]=upd*tag[y]%MOD;
p[y]=upd*p[y]%MOD;
return y;
}
pushdown(x); pushdown(y);
L p1=p[lc[y]],p2=p[lc[x]];
lc[x]=solve(lc[x],lc[y],xs1,(xs2+p[rc[y]])%MOD,ys1,(ys2+p[rc[x]])%MOD);
rc[x]=solve(rc[x],rc[y],(xs1+p1)%MOD,xs2,(ys1+p2)%MOD,ys2);
pushup(x);
return x;
} void dfs(int x){
if(l[x]) dfs(l[x]);
if(r[x]) dfs(r[x]);
if(!l[x]) return;
if(!r[x]) {root[x]=root[l[x]]; return;}
nowp=gailv[x];
root[x]=solve(root[l[x]],root[r[x]],,,,);
}
L w[M]={},hh[M]={}; int m=; L ans=;
void getans(int x,L l,L r){
if(l==r){
ans=(ans+l*hh[l]%MOD*p[x]%MOD*p[x])%MOD;
return;
}
pushdown(x);
L mid=(l+r)>>;
getans(lc[x],l,mid);
getans(rc[x],mid+,r);
} int main(){
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int n; scanf("%d",&n);
for(int i=;i<=n;i++){
int x; scanf("%d",&x);
if(!l[x]) l[x]=i;
else r[x]=i;
}
L inv10000=pow_mod(,MOD-);
for(int i=;i<=n;i++){
L x; scanf("%lld",&x);
if(l[i]) gailv[i]=x*inv10000%MOD;
else w[i]=x,hh[++m]=x;
}
sort(hh+,hh+m+);
for(int i=;i<=n;i++) if(w[i]){
int x; x=lower_bound(hh+,hh+m+,w[i])-hh;
updata(root[i],,m,x);
}
dfs();
getans(root[],,m);
printf("%lld\n",ans);
}
【pkuwc2018】 【loj2537】 Minmax DP+线段树合并的更多相关文章
- [BZOJ5461][LOJ#2537[PKUWC2018]Minimax(概率DP+线段树合并)
还是没有弄清楚线段树合并的时间复杂度是怎么保证的,就当是$O(m\log n)$吧. 这题有一个显然的DP,dp[i][j]表示节点i的值为j的概率,转移时维护前缀后缀和,将4项加起来就好了. 这个感 ...
- LOJ2537. 「PKUWC2018」Minimax【概率DP+线段树合并】
LINK 思路 首先暴力\(n^2\)是很好想的,就是把当前节点概率按照权值大小做前缀和和后缀和然后对于每一个值直接在另一个子树里面算出贡献和就可以了,注意乘上选最大的概率是小于当前权值的部分,选最小 ...
- BZOJ.5461.[PKUWC2018]Minimax(DP 线段树合并)
BZOJ LOJ 令\(f[i][j]\)表示以\(i\)为根的子树,权值\(j\)作为根节点的概率. 设\(i\)的两棵子树分别为\(x,y\),记\(p_a\)表示\(f[x][a]\),\(p_ ...
- 【洛谷5298】[PKUWC2018] Minimax(树形DP+线段树合并)
点此看题面 大致题意: 有一棵树,给出每个叶节点的点权(互不相同),非叶节点\(x\)至多有两个子节点,且其点权有\(p_x\)的概率是子节点点权较大值,有\(1-p_x\)的概率是子节点点权较小值. ...
- [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\)时刻这 ...
- 洛谷P4577 [FJOI2018]领导集团问题(dp 线段树合并)
题意 题目链接 Sol 首先不难想到一个dp,设\(f[i][j]\)表示\(i\)的子树内选择的最小值至少为\(j\)的最大个数 转移的时候维护一个后缀\(mx\)然后直接加 因为后缀max是单调不 ...
- BZOJ 5469: [FJOI2018]领导集团问题 dp+线段树合并
在 dp 问题中,如果发现可以用后缀最大值来进行转移的话可以考虑去查分这个后缀最大值. 这样的话可以用差分的方式来方便地进行维护 ~ #include <bits/stdc++.h> #d ...
- LOJ2537 PKUWC2018 Minimax 树形DP、线段树合并
传送门 题意:自己去看 首先可以知道,每一个点都有几率被选到,所以$i$与$V_i$的关系是确定了的. 所以我们只需要考虑每一个值的取到的概率. 很容易设计出一个$DP$:设$f_{i,j}$为在第$ ...
随机推荐
- js动态添加删除行,兼容ie和火狐
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- UVa 11178 Morley's Theorem (几何问题)
题意:给定三角形的三个点,让你求它每个角的三等分线所交的顶点. 析:根据自己的以前的数学知识,应该很容易想到思想,比如D点,就是应该求直线BD和CD的交点, 以前还得自己算,现在计算机帮你算,更方便, ...
- PAT 甲 1005. Spell It Right (20) 2016-09-09 22:53 42人阅读 评论(0) 收藏
1005. Spell It Right (20) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue Given ...
- 老刘 Yii2 源码学习笔记之 Module 类
关系类图 从上图可以看出 Application 类继承了 Module,在框架中的是非常重要角色. 加载配置 public function setModules($modules) { forea ...
- 结对编程--四则运算(Java)梅进鹏 欧思良
结对编程--四则运算(Java)梅进鹏 欧思良 Github项目地址:https://github.com/MeiJinpen/Arithmetic 功能要求 题目:实现一个自动生成小学四则运算题目的 ...
- ORACLE报表触发器
http://www.cnblogs.com/quanweiru/archive/2012/09/26/2704308.html 触发器一.报表触发器(report trigger)报表触发器主要用于 ...
- Android-Git命令行操作
Git命令行操作,在Mac上使用的话,Mac会自带了Git,直接在终端或者iTerm都可以执行Git命令操作: Git命令行操作,在Windows系统电脑上使用的话,需要安装Git,安装好Git ...
- ServiceBase.OnStart 方法
msdn 解释 派生类中实现时,在由服务控制管理器 (SCM) 或在操作系统启动时 (对于自动启动的服务) 时,将启动命令发送到服务时执行. 指定当服务启动时要执行的操作. 命名空间: Syste ...
- RabbitMQ基础入门篇
下载安装 Erlang RabbitMQ 启动RabbitMQ管理平台插件 DOS下进入到安装目录\sbin,执行以下命令 rabbitmq-plugins enable rabbitmq_manag ...
- NetCore入门篇:(一)Net Core环境安装
一.下载Visual Studio 2017 1.下载地址:https://www.visualstudio.com/zh-hans/downloads/ 2.选择要下载的版本 二.安装Net Cor ...