Luogu P5298 PKUWC2018 Minimax 题解 [ 紫 ] [ 树形 dp ] [ 线段树合并 ] [ 概率 dp ]
Minimax:线段树合并优化 dp 好题。
树形 dp
因为要求出每一个值的出现概率,首先我们可以想到一个很暴力的 dp 式子。
定义 \(dp_{i,j}\) 表示在节点 \(i\) 时,权值 \(j\) 的出现概率,设 \(l\) 表示左儿子,\(r\) 表示右儿子,则有如下转移:
- 当 \(j\) 在左儿子中时,\(dp_{i,j}\gets dp_{l,j}\times(p_i\times\sum_{k=1}^{j-1}dp_{r,k}+(1-p_i)\times\sum_{k=j+1}^{V}dp_{r,k})\),理解的话就是对父亲节点选大的还是选小的进行分讨。
- 当 \(j\) 在右儿子中时,\(dp_{i,j}\gets dp_{r,j}\times(p_i\times\sum_{k=1}^{j-1}dp_{l,k}+(1-p_i)\times\sum_{k=j+1}^Vdp_{l,k})\)。
直接转移即可,时间复杂度 \(O(nV)\)。
线段树合并优化
显然原来的时间复杂度会炸掉,但是我们发现每个节点最开始时最多只有一个 dp 位置是有值的,所以我们考虑用这种均摊复杂度的线段树合并来优化这个 dp。
因为 dp 转移的时候需要用到前缀和后缀和,所以我们进行 merge 的时候记录节点 \(x,y\) 的前缀和 \(px,py\) 与后缀和 \(sx,sy\) 以及父亲节点的概率 \(p\)。
梳理一下 merge 的流程:
- 进入节点 \(x,y\)。
- 如果 \(x,y\) 其中之一是空树,则说明直接更新 dp 值即可。
- 若 \(x\) 是空树,对应着上述 \(j\) 在右儿子中的转移方式,则我们对 \(y\) 的整颗子树内的 dp 值全部乘上 \((p\times\sum_{k=1}^{j-1}dp_{l,k}+(1-p)\times\sum_{k=j+1}^Vdp_{l,k})=(p\times px+(1-p)\times sx)\) 即可。这个可以用懒标记实现区间乘。
- 若 \(y\) 是空树,对应着上述 \(j\) 在左儿子中的转移方式,则我们对 \(x\) 的整颗子树内的 dp 值全部乘上 \((p\times\sum_{k=1}^{j-1}dp_{r,k}+(1-p)\times\sum_{k=j+1}^Vdp_{r,k})=(p\times py+(1-p)\times sy)\) 即可。这个可以用懒标记实现区间乘。
- 否则就说明要递归合并,递归左右儿子的时候记得更新 \(sx,sy,px,py\) 的值。
- 最后将左右儿子的 dp 值加起来就是这个区间的 dp 值。
时间复杂度 \(O(n\log n)\)。
代码
#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi=pair<int,int>;
const int N=300005;
const ll mod=998244353;
int n,fa[N],m=0,b[N],son[N][2],cd[N],p[N],ans[N];
ll qpow(ll a,ll b)
{
ll res=1;
while(b)
{
if(b&1)res=(res*a)%mod;
b>>=1;
a=(a*a)%mod;
}
return res;
}
int getrk(int x)
{
return (lower_bound(b+1,b+m+1,x)-b);
}
struct Node{
int ls,rs;
ll dp,tag=1;
};
struct Segtree{
Node tr[20*N];
int root[N],tot=0;
void pushup(int p)
{
tr[p].dp=(tr[lc(p)].dp+tr[rc(p)].dp)%mod;
}
void pushdown(int p)
{
if(tr[p].tag!=1)
{
tr[lc(p)].tag=(tr[lc(p)].tag*tr[p].tag)%mod;
tr[rc(p)].tag=(tr[rc(p)].tag*tr[p].tag)%mod;
tr[lc(p)].dp=(tr[lc(p)].dp*tr[p].tag)%mod;
tr[rc(p)].dp=(tr[rc(p)].dp*tr[p].tag)%mod;
}
tr[p].tag=1;
}
void modify(int p,int v)
{
tr[p].dp=(tr[p].dp*1ll*v)%mod;
tr[p].tag=(tr[p].tag*1ll*v)%mod;
}
void update(int &u,int ln,int rn,int x,ll k)
{
if(u==0)u=++tot;
if(ln==rn){tr[u].dp+=k;return;}
int mid=(ln+rn)>>1;
if(x<=mid)update(lc(u),ln,mid,x,k);
else update(rc(u),mid+1,rn,x,k);
pushup(u);
}
int merge(int x,int y,int px,int py,int sx,int sy,int p)
{
if(x==0&&y==0)return 0;
if(x==0)
{
modify(y,(1ll*p*px%mod+1ll*((1-p)%mod+mod)%mod*sx)%mod);
return y;
}
if(y==0)
{
modify(x,(1ll*p*py%mod+1ll*((1-p)%mod+mod)%mod*sy)%mod);
return x;
}
pushdown(x);pushdown(y);
int lx=tr[lc(x)].dp,rx=tr[rc(x)].dp,ly=tr[lc(y)].dp,ry=tr[rc(y)].dp;
tr[x].ls=merge(lc(x),lc(y),px,py,(sx+rx)%mod,(sy+ry)%mod,p);
tr[x].rs=merge(rc(x),rc(y),(px+lx)%mod,(py+ly)%mod,sx,sy,p);
pushup(x);
return x;
}
void query(int u,int ln,int rn)
{
if(ln==rn){ans[ln]=tr[u].dp;return;}
int mid=(ln+rn)>>1;
pushdown(u);
query(lc(u),ln,mid);
query(rc(u),mid+1,rn);
}
}tr1;
void dfs1(int u)
{
if(son[u][0]==0)
{
tr1.update(tr1.root[u],1,m,getrk(p[u]),1);
return;
}
if(son[u][1]==0)
{
dfs1(son[u][0]);
tr1.root[u]=tr1.root[son[u][0]];
return;
}
dfs1(son[u][0]);
dfs1(son[u][1]);
tr1.root[u]=tr1.merge(tr1.root[son[u][0]],tr1.root[son[u][1]],0,0,0,0,p[u]);
}
int main()
{
//freopen("sample.in","r",stdin);
//freopen("sample.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++)cin>>fa[i];
for(int i=1;i<=n;i++)
{
son[fa[i]][cd[fa[i]]]=i;
cd[fa[i]]++;
}
for(int i=1;i<=n;i++)
{
cin>>p[i];
if(cd[i])p[i]=p[i]*1ll*qpow(10000,mod-2)%mod;
else b[++m]=p[i];
}
sort(b+1,b+m+1);
m=unique(b+1,b+m+1)-b-1;
dfs1(1);
tr1.query(tr1.root[1],1,m);
ll res=0;
for(int i=1;i<=m;i++)res=(res+1ll*i*b[i]%mod*ans[i]%mod*ans[i]%mod)%mod;
cout<<res;
return 0;
}
Luogu P5298 PKUWC2018 Minimax 题解 [ 紫 ] [ 树形 dp ] [ 线段树合并 ] [ 概率 dp ]的更多相关文章
- BZOJ4919 大根堆(树形dp+线段树合并)
用 multiset 启发式合并贪心维护 LIS 的做法就不多说了,网上题解一大堆,着重讲一下线段树合并维护 \(dp\). \(O(n^2)\) 的 \(dp\) 非常显然.离散化后,设 \(dp[ ...
- Luogu P5298 [PKUWC2018]Minimax
好劲的题目啊,根本没往线段树合并方面去想啊 首先每种权值都有可能出现,因此我们先排个序然后一个一个求概率 由于此时数的值域变成\([1,m]\)(离散以后),我们可以设一个DP:\(f_{x,i}\) ...
- [BZOJ5461][LOJ#2537[PKUWC2018]Minimax(概率DP+线段树合并)
还是没有弄清楚线段树合并的时间复杂度是怎么保证的,就当是$O(m\log n)$吧. 这题有一个显然的DP,dp[i][j]表示节点i的值为j的概率,转移时维护前缀后缀和,将4项加起来就好了. 这个感 ...
- LOJ #2537. 「PKUWC 2018」Minimax (线段树合并 优化dp)
题意 小 \(C\) 有一棵 \(n\) 个结点的有根树,根是 \(1\) 号结点,且每个结点最多有两个子结点. 定义结点 \(x\) 的权值为: 1.若 \(x\) 没有子结点,那么它的权值会在输入 ...
- 【pkuwc2018】 【loj2537】 Minmax DP+线段树合并
今年年初的时候参加了PKUWC,结果当时这一题想了快$2h$都没有想出来.... 哇我太菜啦.... 昨天突然去搜了下哪里有题,发现$loj$上有于是就去做了下. 结果第一题我5分钟就把所有细节都想好 ...
- P6847-[CEOI2019]Magic Tree【dp,线段树合并】
正题 题目链接:https://www.luogu.com.cn/problem/P6847 题目大意 \(n\)个点的一棵树上,每个时刻可以割掉一些边,一些节点上有果实表示如果在\(d_i\)时刻这 ...
- 【洛谷5298】[PKUWC2018] Minimax(树形DP+线段树合并)
点此看题面 大致题意: 有一棵树,给出每个叶节点的点权(互不相同),非叶节点\(x\)至多有两个子节点,且其点权有\(p_x\)的概率是子节点点权较大值,有\(1-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+线段树合并】
LINK 思路 首先暴力\(n^2\)是很好想的,就是把当前节点概率按照权值大小做前缀和和后缀和然后对于每一个值直接在另一个子树里面算出贡献和就可以了,注意乘上选最大的概率是小于当前权值的部分,选最小 ...
- [PKUWC2018]Minimax [dp,线段树合并]
好妙的一个题- 我们设 \(f_{i,j}\) 为 \(i\) 节点出现 \(j\) 的概率 设 \(l = ch[i][0] , r = ch[i][1]\) 即左儿子右儿子 设 \(m\) 为叶子 ...
随机推荐
- k8s之基础篇
相关概念: kube-apierver: 控制平面组件,负责kubernetes api, 处理接受的请求工作 kube-controller-manager: 控制平面组件, 负责运行控制器进程 k ...
- http相关知识要点
1.TCP/IP协议分为哪几层?每一层主要作用是什么?为什么要分层? 应用层 传输层 网络层 数据链路层 2.HTTP请求有哪些方式? GET:用于从服务器获取资源.不会改变资源状态,无副作用,幂等. ...
- 销讯通CRM客户关系管理系统的功能拆分
随着技术的发展,CRM系统(客户关系管理)成为企业不可或缺的工具,本文从医药行业角度简要谈谈CRM系统的功能. 从业务的理解来说,从医药行业来说,CRM客户管理系统的有以下几部分功能: 01 客户的分 ...
- C++中的多线程及其之后的周边
多线程 参考:https://zhuanlan.zhihu.com/p/613630658 平台差异:Linux 与 Windows,跨平台方案 在Linux上,有pthread的使用,而C++ 11 ...
- 藏不住了,这届数字打工人(RPA)想在各行各业“当骨干”!
数字化时代,最红的"打工人"是谁? 无疑,是RPA(数字化劳动力). 这种由机器人流程自动化+AI驱动,模拟人点击.移动鼠标等在电脑上的操作,在各种规则明确.高重复度业务场景中代替 ...
- Qt编写地图综合应用50-获取区域边界
一.前言 区域边界也是一些坐标点集合,而且不同的行政区划得到的区域边界点数组集合个数不同,觉得部分都是一个集合,少部分有一些飞地之类的,需要多个闭合区域,所以会得到多个数组集合,绘制的时候都要分别取出 ...
- Cesium 在线地图访问总结
参考:https://deyihu.github.io/src/maptalks-tileLayercollection/examples/?tdsourcetag=s_pcqq_aiomsg 以下u ...
- IM群聊消息的已读未读功能在存储空间方面的实现思路探讨
1.引言 IM系统中,特别是在企业应用场景下,消息的已读未读状态是一个强需求. 以阿里的钉钉为例,钉钉的产品定位是用于商务交流,其"强制已读回执"功能,让职场人无法再"假 ...
- 内华达大地测量实验室GNSS数据tenv3格式下载
GNSS时序形变位移数据下载 引言 下载方式 注意事项 引言 目的:教大家如何下载GNSS时序形变位移数据,本人主要是利用GNSS位移结果进行InSAR相关成果的精度验证工作.若大家需要在自己的研究领 ...
- 在 ASP.NET Core WebAPI如何实现版本控制?
在 ASP.NET Core WebAPI 中实现版本控制(Versioning)是一种常见的做法,用于管理 API 的演进和兼容性.通过版本控制,我们可以在不破坏现有客户端的情况下引入新功能或修改现 ...