点此看题面

大致题意: 有一棵树,给出每个叶节点的点权(互不相同),非叶节点\(x\)至多有两个子节点,且其点权有\(p_x\)的概率是子节点点权较大值,有\(1-p_x\)的概率是子节点点权较小值。假设根节点\(1\)号节点的点权有\(m\)种可能性,其中权值第\(i\)小的可能点权是\(V_i\),可能性为\(D_i\),求\(\sum_{i=1}^mi\cdot V_i\cdot D_i^2\)。

前言

好妙的题目,像我这种蒟蒻根本想不到线段树合并还可以这么玩。

同时,在无数个地方漏掉\(PushDown\)的我感觉自己真是弱到连线段树都不会了......

题意转化

由于题目中保证\(0<p_x<1\),所以每种点权都可能被取到。

如果我们将点权排个序,那么\(i\)和\(V_i\)都是显然的,只要想个办法求出\(D_i\)即可。

推式子

首先,我们把权值离散化。

设\(f_{x,i}\)表示节点\(x\)的权值为\(i\)的概率,则\(D_i=f_{1,i}\)。

考虑如何转移。

如果\(x\)没有子节点,设其给定权值为\(v\),那么\(f_{x,i}=[i=v]\)。

如果\(x\)只有一个子节点,设其为\(son\),那么\(f_{x,i}=f_{son,i}\)。

如果\(x\)有两个子节点,分别为\(lc\)和\(rc\)。

题目告诉我们,权值是互不相同的。

则对于一个权值\(i\),若其满足\(f_{lc,i}>0\),就说明这个权值在左子树中。

下面便以在左子树中的权值\(i\)为例,讲讲\(f_{x,i}\)如何转移,而在右子树中是同理的。

我们知道,有\(p_x\)的概率,\(x\)的权值取较大值,则此时\(i\)应大于右儿子的权值,即概率为\(p_x(\sum_{k=1}^{i-1}f_{rc,k})\)。

同理,有\((1-p_x)\)的概率,\(x\)的权值取较小值,则此时\(i\)应小于右儿子的权值,即概率为\((1-p_x)(\sum_{k=i+1}^mf_{rc,k})\)。

而这个概率实际上还要乘上\(f_{lc,i}\),所以:

\[f_{x,i}=f_{lc,i}\cdot(p_x(\sum_{k=1}^{i-1}f_{rc,k})+(1-p_x)(\sum_{k={i+1}}^mf_{rc,k}))
\]

线段树分治

仔细观察上面的式子,就可以发现,\(\sum_{k=1}^{i-1}f_{rc,k}\)和\(\sum_{k={i+1}}^mf_{rc,k}\)分别是前缀和与后缀和。

现在我们需要一个数据结构,能够维护数组之间的转移,还在前缀和与后缀和的维护方面有优势,于是不难想到动态开点线段树。

而这个转移的过程,可以用线段树合并来实现。

用线段树合并有什么好处呢?

就是在线段树合并的过程中,我们可以同时维护下\(f_{lc}\)和\(f_{rc}\)的前缀和与后缀和,这样就能很方便地进行转移了。

当遇到某一个节点,在一棵树中为空,一棵树中非空,我们就可以借助维护下的前缀和与后缀和,打一个乘法标记,便可完成转移了。

具体实现详见代码。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 300000
#define LN 20
#define X 998244353
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
using namespace std;
int n,ee,dc,a[N+5],dv[N+5],Rt[N+5],lnk[N+5];struct edge {int to,nxt;}e[N+5];
I int Qpow(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define tn (x<<3)+(x<<1)
#define D isdigit(c=tc())
char c,*A,*B,FI[FS];
public:
I FastIO() {A=B=FI;}
Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
}F;
template<int PS> class SegmentTree//动态开点线段树
{
private:
#define PD(x) F[x]^1&&\
(\
V[S[x][0]]=1LL*V[S[x][0]]*F[x]%X,F[S[x][0]]=1LL*F[S[x][0]]*F[x]%X,\
V[S[x][1]]=1LL*V[S[x][1]]*F[x]%X,F[S[x][1]]=1LL*F[S[x][1]]*F[x]%X,F[x]=1\
)
int Nt,V[PS+5],F[PS+5],S[PS+5][2];
public:
I void Ins(int& rt,CI x,CI l=1,CI r=dc)//插入
{
if(rt=++Nt,V[rt]=F[rt]=1,l==r) return;int mid=l+r>>1;
x<=mid?Ins(S[rt][0],x,l,mid):Ins(S[rt][1],x,mid+1,r);
}
I int Qry(CI rt,CI x,CI l=1,CI r=dc)//询问
{
if(l==r) return V[rt];int mid=l+r>>1;PD(rt);
return x<=mid?Qry(S[rt][0],x,l,mid):Qry(S[rt][1],x,mid+1,r);
}
I int Merge(CI x,CI y,CI p,CI l=1,CI r=dc,CI lx=0,CI rx=0,CI ly=0,CI ry=0)//线段树合并
{
if(!x&&!y) return 0;RI rt=++Nt;//皆为空直接返回
if(!y) return PD(x),F[rt]=(1LL*p*ly+1LL*(1-p+X)*ry)%X,//修改,注意修改前下传标记
V[rt]=1LL*F[rt]*V[x]%X,S[rt][0]=S[x][0],S[rt][1]=S[x][1],rt;//拷贝信息
if(!x) return PD(y),F[rt]=(1LL*p*lx+1LL*(1-p+X)*rx)%X,//修改,注意修改前下传标记
V[rt]=1LL*F[rt]*V[y]%X,S[rt][0]=S[y][0],S[rt][1]=S[y][1],rt;//拷贝信息
int mid=l+r>>1;PD(x),PD(y),F[rt]=1,//先下传标记
S[rt][0]=Merge(S[x][0],S[y][0],p,l,mid,lx,(rx+V[S[x][1]])%X,ly,(ry+V[S[y][1]])%X),//递归处理左子树
S[rt][1]=Merge(S[x][1],S[y][1],p,mid+1,r,(lx+V[S[x][0]])%X,rx,(ly+V[S[y][0]])%X,ry);//递归处理右子树
return V[rt]=(V[S[rt][0]]+V[S[rt][1]])%X,rt;//注意上传信息
}
};SegmentTree<N*LN<<1> S;
I void dfs(CI x)
{
if(!lnk[x]) return S.Ins(Rt[x],lower_bound(dv+1,dv+dc+1,a[x])-dv);//处理叶节点,注意离散化
for(RI i=lnk[x];i;i=e[i].nxt) dfs(e[i].to),//先处理子节点
Rt[x]=Rt[x]?S.Merge(Rt[x],Rt[e[i].to],a[x]):Rt[e[i].to];//合并子节点
}
int main()
{
RI i,x,ans;for(F.read(n),i=1;i<=n;++i) F.read(x),x&&add(x,i);
for(i=1;i<=n;++i) F.read(a[i]),lnk[i]?(a[i]=1LL*a[i]*Qpow(10000,X-2)%X):(dv[++dc]=a[i]);
sort(dv+1,dv+dc+1),dfs(1);//排序
for(ans=0,i=1;i<=dc;++i) x=S.Qry(Rt[1],i),ans=(1LL*i*dv[i]%X*x%X*x+ans)%X;//统计答案
return printf("%d",ans),0;
}

【洛谷5298】[PKUWC2018] Minimax(树形DP+线段树合并)的更多相关文章

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

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

  2. 洛谷P3066 [USACO12DEC]逃跑的Barn (线段树合并)

    题目描述It's milking time at Farmer John's farm, but the cows have all run away! Farmer John needs to ro ...

  3. 洛谷P1600 天天爱跑步(线段树合并)

    小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一一棵包含 nn ...

  4. 洛谷P3224 [HNOI2012]永无乡(线段树合并+并查集)

    题目描述 永无乡包含 nnn 座岛,编号从 111 到 nnn ,每座岛都有自己的独一无二的重要度,按照重要度可以将这 nnn 座岛排名,名次用 111 到 nnn 来表示.某些岛之间由巨大的桥连接, ...

  5. 洛谷 P2824 [HEOI2016/TJOI2016]排序 (线段树合并)

    (另外:题解中有一种思路很高妙而且看上去可以适用一些其他情况的离线方法) 线段树合并&复杂度的简单说明:https://blog.csdn.net/zawedx/article/details ...

  6. 洛谷P4556 雨天的尾巴(线段树合并)

    洛谷P4556 雨天的尾巴 题目链接 题解: 因为一个点可能存放多种物品,直接开二维数组进行统计时间.空间复杂度都不能承受.因为每一个点所拥有的物品只与其子树中的点有关,所以可以考虑对每一个点来建立一 ...

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

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

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

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

  9. LOJ2537. 「PKUWC2018」Minimax【概率DP+线段树合并】

    LINK 思路 首先暴力\(n^2\)是很好想的,就是把当前节点概率按照权值大小做前缀和和后缀和然后对于每一个值直接在另一个子树里面算出贡献和就可以了,注意乘上选最大的概率是小于当前权值的部分,选最小 ...

随机推荐

  1. Redis面试篇 -- Redis常见性能问题和解决方案?

    master最好不要做任何的持久化工作,如RD内存快照或者AOF日志文件: 如果数据比较重要,某个slave开始AOF备份数据,策略设置为每秒同步1次: 为了主从复制的速度和连接的稳定性,master ...

  2. JQuery 常用网址

    http://www.bejson.com/apidoc/jquery/css.html  操作手册 https://jquery.com/   JQuery官网 一.JQuery插件的网站 1.ht ...

  3. react-router刷新页面Cannot GET 问题

    最近在做项目的时候遇到了如下错误 并在控制台看到了如下的报错 我先是按照控制台的错误搜索,得出的结果都是对meta头部进行设置,允许资源请求,但是问题依然没有解决,偶然间改变了想法,会不会是路由的问题 ...

  4. [20191125]oracel SQL parsing function qcplgte 2.txt

    [20191125]oracel SQL parsing function qcplgte 2.txt --//参考前面的测试:http://blog.itpub.net/267265/viewspa ...

  5. 将 云数据库MongoDB(阿里云)物理备份文件下载恢复至本地自建数据库 遇到的5个问题

    有时候我们可能需要将云上数据库下载到本地,下面是我们在操作MongoDB数据库时遇到的五个小问题. 其实现在RDS的 帮助文档 写的都比较详细了,大家在第一次操作时,可以细读一下,避免一些不必要的问题 ...

  6. mybatis+sqlServer 实现insertOrUpdate

    这两天遇到一个头疼的问题,我们系统需要请求第三方数据,第三方收到请求后会生成相应的数据并入库,我们通过定时任务将第三方数据同步到我们数据库.当我们发送请求后第三方会立即返回一个值,我们会根据返回值去数 ...

  7. @ImportResource

    1. @ImportResource(locations = {"classpath:beantest.xml"})标注到启动类上,从类路径下加载xml文件,通过Applicati ...

  8. python 导入同级目录文件时报错

    当你import的时候,python解释器只会在sys.path这个变量(一个list,你可以print出来看)里面的路径中找可能匹配的package或module. 而一个package跟一个普通文 ...

  9. IT兄弟连 HTML5教程 CSS3揭秘 CSS3属性2

    3  背景属性 在CSS3中提供了多个背景属性,这里只介绍两个比较常用的属性,其他属性可以从手册中获取帮助.在CSS3中,通过background-image或者background属性可以为一个容器 ...

  10. C sharp #002# 结构化编程基础

    饮水思源:金老师的自学网站.C# Guide 索引 变量与数据类型 C#中For each的写法 C#控制台程序编程技巧 简易图片浏览器 BigInteger以及浮点数的比较 一.变量与数据类型 us ...