传送门:Here

一道线段树合并好题

如果要维护点$ x$的信息,相当于合并$ x$的两棵子树

对于这题显然有:任何叶子节点的权值都可能出现在其祖先上

因而我们只需要在线段树合并的时候维护概率即可

我们令$ maxa(i)$表示在左子树中权值比i大的概率,$ maxb(i)$表示在右子树中权值比i大的概率,$ pL$表示这个节点选择较大权值为最终结果的概率

若一个出现在左子树的权值v成为了最终权值,概率应为$ maxb(v)*(1-pL)+(1-maxb(v))*pL)=maxb(v)+pL-2*maxb(v)*pL$

同理一个出现在右子树的权值v成为最终权值的概率应为$ maxa(v)*(1-pL)+(1-maxa(v))*pL)=maxa(v)+pL-2*maxa(v)*pL$

直接暴力枚举线段树上所有节点更新权值效率低下,考虑如何在合并过程中完成$ maxa$和$ maxb$的维护

我们优先合并右子树,即从大到小合并,初始可以认为不存在比自己大的也就是$ maxa=maxb=0$

合并两棵树的时候由于优先合并右边,可以保证左边均没有被合并过而右边已经合并完全

这时候$ maxa$和$ maxb$恰好就是对应的函数值,然后更新$ maxa$或$ maxb$的值(加上这段区间里出现的概率)

同普通线段树合并,如果发现某棵子树为空,则给另一颗子树的所有概率乘上$ maxa$或$ maxb$,以标记形式下传

最后遍历根节点所对应的线段树求出答案即可

code:

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 300010
#define rt register int
#define l putchar('\n')
#define ll long long
#define r read()
#define p 998244353
using namespace std;
inline ll read()
{
register ll x = ; char zf = ; char ch;
while (ch != '-' && !isdigit(ch)) ch = getchar();
if (ch == '-') zf = -, ch = getchar();
while (isdigit(ch)) x = x * + ch - '', ch = getchar(); return x * zf;
}int i,j,k,m,n,x,y,z,cnt;
int fa[M],sum[M],son[M][];
int pmax[M],v[M],Root[M],ys[M];
struct seg_ment{
int L,R,ls,rs,gl,fla;
}a[*M];
void insert(int &x,const int L,const int R,const int val)
{
x=++cnt;a[x].L=L;a[x].R=R;a[x].gl=;a[x].fla=;
if(L==R)return;
const int mid=L+R>>;
if(val<=mid)insert(a[x].ls,L,mid,val);
else insert(a[x].rs,mid+,R,val);
}
int maxa,maxb;
void down(const int x)
{
if(a[x].fla>)
{
a[x].gl=(ll)a[x].gl*a[x].fla%p;
a[a[x].ls].fla=(ll)a[a[x].ls].fla*a[x].fla%p;
a[a[x].rs].fla=(ll)a[a[x].rs].fla*a[x].fla%p;
a[x].fla=;
}
}
int merge(int x,int y,int pmax)//合并操作
{
if(!x&&!y)return ;
down(x);down(y);
if(!x)
{
maxb=(maxb+a[y].gl)%p;//更新maxb的值
a[y].fla*=(maxa+pmax-2ll*maxa*pmax%p+p)%p;
down(y);
return y;
}
if(!y)
{
maxa=(maxa+a[x].gl)%p;//更新maxa的值
a[x].fla*=(maxb+pmax-2ll*maxb*pmax%p+p)%p;
down(x);
return x;
}
int d1=a[a[x].rs].gl,d2=a[a[y].rs].gl;
a[x].rs=merge(a[x].rs,a[y].rs,pmax);
a[x].ls=merge(a[x].ls,a[y].ls,pmax);
a[x].gl=(a[a[x].ls].gl+a[a[x].rs].gl)%p;
return x;
} void dfs(const int x)//以dfs顺序完成线段树合并
{
if(!sum[x])return;
if(sum[x]==)dfs(son[x][]),Root[x]=Root[son[x][]];
if(sum[x]==)
{
dfs(son[x][]);
dfs(son[x][]);
maxa=maxb=;
Root[x]=merge(Root[son[x][]],Root[son[x][]],pmax[x]);
}
}
ll ans=;
void getans(const int x,const int L,const int R)//遍历求答案
{
if(!a[x].gl)return;
down(x);
if(L==R)
{
cnt++;
ans=(ans+(ll)cnt*ys[L]%p*a[x].gl%p*a[x].gl%p)%p;
return;
}
const int mid=L+R>>;
getans(a[x].ls,L,mid);getans(a[x].rs,mid+,R);
}
struct node{
int x,id;
bool operator <(const node s)const{
return x<s.x;
}
}Q[];int top;//离散化
int main()
{
n=read();
for(rt i=;i<=n;i++)
{
fa[i]=read();
son[fa[i]][sum[fa[i]]++]=i;
}
for(rt i=;i<=n;i++)
if(sum[i])pmax[i]=r*%p;//这是10000模998244353下逆元
else Q[++top]=(node){read(),i};
sort(Q+,Q+top+); for(rt i=;i<=top;i++)
ys[i]=Q[i].x,insert(Root[Q[i].id],,top,i);
dfs();cnt=;
getans(Root[],,top);cout<<ans;
return ;
}

「PKUWC 2018」Minimax的更多相关文章

  1. LOJ #2537. 「PKUWC 2018」Minimax (线段树合并 优化dp)

    题意 小 \(C\) 有一棵 \(n\) 个结点的有根树,根是 \(1\) 号结点,且每个结点最多有两个子结点. 定义结点 \(x\) 的权值为: 1.若 \(x\) 没有子结点,那么它的权值会在输入 ...

  2. loj2537 「PKUWC 2018」Minimax

    pkusc 快到了--做点题涨涨 rp. 初见时 yy 了一个类似于归并的东西,\(O(n^2)\),50 分. 50 分 yy 做法 对于一个点,枚举他能到达的权值(假设这个权值在左子树,在右子树是 ...

  3. LOJ #2542. 「PKUWC 2018」随机游走(最值反演 + 树上期望dp + FMT)

    写在这道题前面 : 网上的一些题解都不讲那个系数是怎么推得真的不良心 TAT (不是每个人都有那么厉害啊 , 我好菜啊) 而且 LOJ 过的代码千篇一律 ... 那个系数根本看不出来是什么啊 TAT ...

  4. LOJ #2541. 「PKUWC 2018」猎人杀(容斥 , 期望dp , NTT优化)

    题意 LOJ #2541. 「PKUWC 2018」猎人杀 题解 一道及其巧妙的题 , 参考了一下这位大佬的博客 ... 令 \(\displaystyle A = \sum_{i=1}^{n} w_ ...

  5. LOJ #2540. 「PKUWC 2018」随机算法(概率dp)

    题意 LOJ #2540. 「PKUWC 2018」随机算法 题解 朴素的就是 \(O(n3^n)\) dp 写了一下有 \(50pts\) ... 大概就是每个点有三个状态 , 考虑了但不在独立集中 ...

  6. LOJ #2538. 「PKUWC 2018」Slay the Spire (期望dp)

    Update on 1.5 学了 zhou888 的写法,真是又短又快. 并且空间是 \(O(n)\) 的,速度十分优秀. 题意 LOJ #2538. 「PKUWC 2018」Slay the Spi ...

  7. loj2538 「PKUWC 2018」Slay the Spire

    pkusc 快到了--做点题涨涨 rp. ref我好菜啊QAQ. 可以发现期望只是一个幌子.我们的目的是:对于所有随机的选择方法(一共 \(\binom{2n}{m}\)种),这些选择方法都最优地打出 ...

  8. loj2540 「PKUWC 2018」随机算法

    pkusc 快到了--做点题涨涨 rp. 记 \(f(S,i)\) 表示 \(S\) 这个集合是决计不能选的(要么属于独立集,要么和独立集相连),或称已经考虑了的,\(i\) 表示此集合对应的最大独立 ...

  9. 「PKUWC 2018」随机算法 (第二版,正解做法)

    上一版貌似是打了 O(3 ^ N) 暴力和 一条链的情况,得了60分.... 第一次做的时候光想练一练暴力...就没去想正解,谁知道正解比暴力好写不知道多少,mmp 设 f(S) 为 选集合S中的点可 ...

随机推荐

  1. 震撼:多线程下的操作离不开synchronized

    昨天在写一个聊天程序,在发送消息的时候是采用单独的一个线程,接收消息是在另一个线程中完成. 我在测试的过程中发现,有的时候当消息比较多时,比如: 当我刚刚发送完一条消息,这个时候要将我发送的消息添加到 ...

  2. Day18--Python--面向对象--类与类之间的关系

    1. 依赖关系 在方法中引入另一个类的对象 (最不紧密的一种关系) 依赖关系都是把另一个对象当做参数 class Elephant: def __init__(self, name): self.na ...

  3. terminate called without an active exception异常

    在gcc4.4下,采用回调机制写了一个类似std::thread的线程类. 但是使用时却发生了核心已转移的错误. main函数调用的代码大致是 int main(int argc, char *arg ...

  4. tcping 与 telnet命令粗略使用

        使用tcping命令,在网上下载tcping文件,放入c盘的system32目录下,即可使用 使用tcping命令用来ping某个端口,能通的话,说明从外部到端口是没有问题的 使用telnet ...

  5. Excel:6种多条件查找方法

    如下图所示,要求根据设备分类和品牌来查找相应的销售数量. 1. 使用VLOOKUP+辅助列进行多条件查找 本例采用的方法是在原表的最前面加一辅助列,辅助列的公式为:=B2&C2 然后再采用VL ...

  6. stock1114

    # encoding: utf-8 import requests import logging import logging.config import random import os impor ...

  7. 【JS】js将 /Date(1518943415760+0800)/ 转换为2018-2-18 16:43:35格式

    function formatDate(dt) { var year = dt.getFullYear(); var month = dt.getMonth() + 1; var date = dt. ...

  8. python第三次周末大作业

    ''' s18第三周周末⼤作业 模拟博客园系统: 1. 启动程序, 显⽰菜单列表 菜单: 1. 登录 2. 注册 3. ⽂章 4. ⽇记 5. 退出 2. ⽤户输入选项, ⽂章和⽇记必须在登录后才可以 ...

  9. [JUC-2]AbstractQueuedSynchronizer源码分析

    AbstactQueuedSynchronizer的基本数据结构 AbstractQueuedSynchronizer的基本数据结构为Node,关于Node,JDK作者写了详细的注释,这里我大致总结几 ...

  10. ruby--Hash方法汇总

    一.给Hash添加默认值 :h = {1,2,3,4}    #=> {1 => 2, 3 => 4}    h.default = 7   h[1]                 ...