BZOJ.5461.[PKUWC2018]Minimax(DP 线段树合并)
令\(f[i][j]\)表示以\(i\)为根的子树,权值\(j\)作为根节点的概率。
设\(i\)的两棵子树分别为\(x,y\),记\(p_a\)表示\(f[x][a]\),\(p_a'\)表示\(f[y][a]\),\(P_i\)表示给定的\(i\)取最大值作为权值的概率。
转移就是两棵树之间的权值组合,即以\(x\)子树中的\(a\)作为最小值的概率为\(p_a\times\sum\limits_{v>a}p_v'\times(1-P_i)\),以\(x\)子树中的\(a\)作为最大值的概率为\(p_a\times\sum\limits_{v\leq a}p_v'\times P_i\)。
记\(s'=\sum\limits_{v>a}p_v'\),\(\sum\limits_{v\leq a}p_v'\)就是\(1-s'\)。上面项加起来化简一下就是\(p_a\times(s'+P_i-2s'\times P_i)\)。
对于\(y\)子树中的\(a\)作为\(i\)节点的值的概率同理,是\(p_a'\times(s+P_i-2s\times P_i)\)。那\(f[i][a]\)就等于新的\(p_a,p_a'\)的和。
维护一下后缀和就是\(O(n^2)\)的了。
初始时每个叶节点只有一个初值,一不小心看到可以想到线段树合并就很简单了。
\(s,s'\)就是当前点的右边所有子树中的\(\sum p_v\)和\(\sum p_v'\),合并的时候先合并右子树,累加一下所有叶节点处的\(p_v\)和\(p_v'\)就可以得到了。(当然\(s,s'\)在维护了区间和后也可以直接用个参数传下去。。见代码2)
当然不需要真访问到叶子处,维护一个区间和。
当\(x\)子树在当前节点有值而\(y\)子树不存在当前节点时(merge(x,y) x!=0 && y=0时),显然\(y\)对\(x\)当前节点的子树的贡献都统计过了,也就是\(x\)当前节点的子树内都会乘一个\(s'+P_i-2s'\times P_i\),这个数不会变了,所以直接打个区间乘标记。顺便维护一个\(x\)子树内\(p_v\)的和\(sum[x]\)来直接得到该子树对\(s\)的贡献,即此时\(s\)+=\(sum[x]\)就可以统计所有子节点的\(p_v\)了。
x=0 && y!=0时同理。
复杂度\(O(n\log n)\)。
代码1:
//103008kb 4564ms
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
#define MAXIN 300000
//#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
#define mod 998244353
#define inv 796898467ll//inv(10000)
#define Mod(x) x>=mod&&(x-=mod)
#define Add(x,v) (x+=v)>=mod&&(x-=mod)
typedef long long LL;
const int N=3e5+5;
int root[N],P[N],ref[N],son[N][2];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Segment_Tree
{
#define ls son[x][0]
#define rs son[x][1]
#define lson ls,l,m
#define rson rs,m+1,r
#define S N*19
int tot,rk,s1,s2,son[S][2],p[S],tag[S];
LL Ans;
#undef S
Segment_Tree() {tag[0]=1;}
#define Upd(x,v) p[x]=1ll*p[x]*v%mod, tag[x]=1ll*tag[x]*v%mod
void Insert(int &x,int l,int r,int pos)
{
x=++tot, p[x]=tag[x]=1;
if(l==r) return;
int m=l+r>>1;
pos<=m ? Insert(lson,pos) : Insert(rson,pos);
}
inline void PushDown(int x)
{
int l=ls,r=rs;
if(l) Upd(l,tag[x]);
if(r) Upd(r,tag[x]);
tag[x]=1;
}
int Merge(int x,int y,int P)
{
if(!x && !y) return 0;
if(!x)
{
Add(s2,p[y]);
int v=(s1+P-2ll*s1*P%mod+mod)%mod;//注意+mod放到后面
Upd(y,v);
return y;
}
if(!y)
{
Add(s1,p[x]);
int v=(s2+P-2ll*s2*P%mod+mod)%mod;
Upd(x,v);
return x;
}
if(tag[x]!=1) PushDown(x);
if(tag[y]!=1) PushDown(y);
rs=Merge(rs,son[y][1],P), ls=Merge(ls,son[y][0],P);
p[x]=p[ls]+p[rs], Mod(p[x]);
return x;
}
void GetAns(int x,int l,int r)
{
if(!p[x]) return;
if(l==r)
{
Ans+=1ll*(++rk)*ref[l]%mod*p[x]%mod*p[x]%mod;//++rk=l
return;
}
if(tag[x]!=1) PushDown(x);//...
int m=l+r>>1;
GetAns(lson), GetAns(rson);
}
}T;
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
void DFS(int x)
{
if(son[x][1])
{
DFS(son[x][0]), DFS(son[x][1]);
T.s1=0, T.s2=0, root[x]=T.Merge(root[son[x][0]],root[son[x][1]],P[x]);
}
else if(son[x][0]) DFS(son[x][0]), root[x]=root[son[x][0]];
}
int main()
{
static std::pair<int,int> A[N];
const int n=read();
for(int i=1,x; i<=n; ++i) x=read(), son[x][0]?son[x][1]=i:son[x][0]=i;
int cnt=0;
for(int i=1; i<=n; ++i)
if(son[i][0]) P[i]=inv*read()%mod;
else A[++cnt]=std::make_pair(read(),i);
std::sort(A+1,A+1+cnt);
for(int i=1; i<=cnt; ++i) ref[i]=A[i].first, T.Insert(root[A[i].second],1,cnt,i);
DFS(1), T.GetAns(root[1],1,cnt), printf("%lld\n",T.Ans%mod);
return 0;
}
代码2:
//105344kb 4528ms
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 300000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
#define mod 998244353
#define inv 796898467ll//inv(10000)
#define Mod(x) x>=mod&&(x-=mod)
#define Add(x,v) (x+=v)>=mod&&(x-=mod)
#define Add2(x,y) (x+y>=mod?x+y-mod:x+y)
typedef long long LL;
const int N=3e5+5;
int root[N],P[N],ref[N],son[N][2];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Segment_Tree
{
#define ls son[x][0]
#define rs son[x][1]
#define lson ls,l,m
#define rson rs,m+1,r
#define S N*19
int tot,son[S][2],p[S],tag[S];
LL Ans;
#undef S
Segment_Tree() {tag[0]=1;}
#define Upd(x,v) p[x]=1ll*p[x]*v%mod, tag[x]=1ll*tag[x]*v%mod
void Insert(int &x,int l,int r,int pos)
{
x=++tot, p[x]=tag[x]=1;
if(l==r) return;
int m=l+r>>1;
pos<=m ? Insert(lson,pos) : Insert(rson,pos);
}
inline void PushDown(int x)
{
int l=ls,r=rs;
if(l) Upd(l,tag[x]);
if(r) Upd(r,tag[x]);
tag[x]=1;
}
int Merge(int x,int y,int s1,int s2,int P)
{
if(!x && !y) return 0;
if(!x)
{
int v=(s1+P-2ll*s1*P%mod+mod)%mod;//注意+mod放到后面
Upd(y,v);
return y;
}
if(!y)
{
int v=(s2+P-2ll*s2*P%mod+mod)%mod;
Upd(x,v);
return x;
}
if(tag[x]!=1) PushDown(x);
if(tag[y]!=1) PushDown(y);
ls=Merge(ls,son[y][0],Add2(s1,p[rs]),Add2(s2,p[son[y][1]]),P), rs=Merge(rs,son[y][1],s1,s2,P);
p[x]=p[ls]+p[rs], Mod(p[x]);
return x;
}
void GetAns(int x,int l,int r)
{
if(!p[x]) return;
if(l==r)
{
Ans+=1ll*l*ref[l]%mod*p[x]%mod*p[x]%mod;
return;
}
if(tag[x]!=1) PushDown(x);//...
int m=l+r>>1;
GetAns(lson), GetAns(rson);
}
}T;
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
void DFS(int x)
{
if(son[x][1])
{
DFS(son[x][0]), DFS(son[x][1]);
root[x]=T.Merge(root[son[x][0]],root[son[x][1]],0,0,P[x]);
}
else if(son[x][0]) DFS(son[x][0]), root[x]=root[son[x][0]];
}
int main()
{
static std::pair<int,int> A[N];
const int n=read();
for(int i=1,x; i<=n; ++i) x=read(), son[x][0]?son[x][1]=i:son[x][0]=i;
int cnt=0;
for(int i=1; i<=n; ++i)
if(son[i][0]) P[i]=inv*read()%mod;
else A[++cnt]=std::make_pair(read(),i);
std::sort(A+1,A+1+cnt);
for(int i=1; i<=cnt; ++i) ref[i]=A[i].first, T.Insert(root[A[i].second],1,cnt,i);
DFS(1), T.GetAns(root[1],1,cnt), printf("%lld\n",T.Ans%mod);
return 0;
}
BZOJ.5461.[PKUWC2018]Minimax(DP 线段树合并)的更多相关文章
- [PKUWC2018]Minimax [dp,线段树合并]
好妙的一个题- 我们设 \(f_{i,j}\) 为 \(i\) 节点出现 \(j\) 的概率 设 \(l = ch[i][0] , r = ch[i][1]\) 即左儿子右儿子 设 \(m\) 为叶子 ...
- [BZOJ 1483] [HNOI2009] 梦幻布丁 (线段树合并)
[BZOJ 1483] [HNOI2009] 梦幻布丁 (线段树合并) 题面 N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1 ...
- [BZOJ5461][LOJ#2537[PKUWC2018]Minimax(概率DP+线段树合并)
还是没有弄清楚线段树合并的时间复杂度是怎么保证的,就当是$O(m\log n)$吧. 这题有一个显然的DP,dp[i][j]表示节点i的值为j的概率,转移时维护前缀后缀和,将4项加起来就好了. 这个感 ...
- LOJ2537. 「PKUWC2018」Minimax【概率DP+线段树合并】
LINK 思路 首先暴力\(n^2\)是很好想的,就是把当前节点概率按照权值大小做前缀和和后缀和然后对于每一个值直接在另一个子树里面算出贡献和就可以了,注意乘上选最大的概率是小于当前权值的部分,选最小 ...
- 【洛谷5298】[PKUWC2018] Minimax(树形DP+线段树合并)
点此看题面 大致题意: 有一棵树,给出每个叶节点的点权(互不相同),非叶节点\(x\)至多有两个子节点,且其点权有\(p_x\)的概率是子节点点权较大值,有\(1-p_x\)的概率是子节点点权较小值. ...
- 【pkuwc2018】 【loj2537】 Minmax DP+线段树合并
今年年初的时候参加了PKUWC,结果当时这一题想了快$2h$都没有想出来.... 哇我太菜啦.... 昨天突然去搜了下哪里有题,发现$loj$上有于是就去做了下. 结果第一题我5分钟就把所有细节都想好 ...
- BZOJ.4399.魔法少女LJJ(线段树合并)
BZOJ 注意\(c\leq7\)→_→ 然后就是裸的权值线段树+线段树合并了. 对于取\(\max/\min\)操作可以直接区间修改清空超出范围的值,然后更新到对应位置上就行了(比如对\(v\)取\ ...
- BZOJ[Usaco2017 Jan]Promotion Counting——线段树合并
题目描述 The cows have once again tried to form a startup company, failing to remember from past experie ...
- BZOJ.2212.[POI2011]Tree Rotations(线段树合并)
题目链接 \(Description\) 给定一棵n个叶子的二叉树,每个叶节点有权值(1<=ai<=n).可以任意的交换两棵子树.问最后顺序遍历树得到的叶子权值序列中,最少的逆序对数是多少 ...
随机推荐
- phpstorm2017.2.1破解
今天安装phpstorm时看了网上很多破解方法,基本上都是用http://idea.lanyus.com/ 提供的注册码或者直接在license server上粘贴譬如http://idea.lany ...
- 关于JDBC的总结
1. Spring JDBC子框架是什么 答:就是一个Spring框架内置的持久层框架.既然是一个持久层框架,作用就是对数据库增删改查!! 2. SpringJDBC是使用声明类操作数据库的? 答:J ...
- poj2116 模拟题
不知道错在哪里 /* 给定两个斐波那契表示数,要求用标准化表达方式表达 然后将这两个数加起来,和也用标准化方式表达 思路:显然要将这两个数先用十进制表示,和也用十进制表示 然后在转化成二进制即可 1 ...
- extjs5(项目中文件的加载过程)
现在来看看js类加载过程.如下图所示: 1、首先:浏览器中输入 localhost:1841 ,调用 index.html; <!DOCTYPE HTML> <html> &l ...
- 读C#开发实战1200例子记录-2017年8月14日10:03:55
C# 语言基础应用,注释 "///"标记不仅仅可以为代码段添加说明,它还有一项更重要的工作,就是用于生成自动文档.自动文档一般用于描述项目,是项目更加清晰直观.在VisualStu ...
- Caused by: java.lang.ClassNotFoundException: Didn't find class "io.grpc.helloworldexample.HelloworldActivity" on path: DexPathList
FAQ: Android app 编译好后安装到手机,运行时闪退,报如下错误: java.lang.RuntimeException: Unable to instantiate activity ...
- RSA 非对称加密【转】
演示代码:https://pan.baidu.com/s/10rfSUUDEEHvCDEYH0oEVCw Base64工具类,可以让rsa编码的乱码变成一串字符序列 1 package com.uti ...
- redcontrol for SL 中文化及样式选择
app.xaml.cs public partial class App: Application { public App() { //指定t ...
- NLS_CHARACTERSET和NLS_NCHAR_CHARACTERSET
The NLS_CHARACTERSET is used for CHAR, VARCHAR2, LONG and CLOB columns;The NLS_NCHAR_CHARACTERSET is ...
- Codeforces 359E Neatness
Neatnes dfs一下用set维护能不能走, 进入的时候点亮灯, 回溯的时候灭灯. #include<bits/stdc++.h> #define LL long long #defi ...