[PKUWC2018] Minimax 题解
1.题目描述
有一颗 n n n 个点的二叉树。根为 1 1 1 号节点。
若 x x x 是叶子节点。会给出权值。
若 x x x 不是叶子节点。那么它的权值有 p x p_x px 概率是它子节点的权值的最大值。有 1 − p x 1-p_x 1−px 的概率是它的子节点的权值的最小值。
若 1 1 1 号节点的权值有 m m m 种可能性,去到权值第 i i i 小的概率为 D i D_i Di,权值为 V i V_i Vi。求:
∑ i = 1 m i ⋅ V i ⋅ D i 2 \sum\limits_{i=1}^{m} i\cdot V_i\cdot D_i^2 i=1∑mi⋅Vi⋅Di2
2.Solution
首先肯定每个值都有概率被取到。
肯定直接 dp 转移是可做的。
对于权值离散化。直接计算每个权值的出现概率。
令
f
u
,
i
f_{u,i}
fu,i 表示点
u
u
u 选择权值
i
i
i 的概率。
f
u
,
i
=
f
l
,
i
×
(
p
u
∑
j
=
1
i
−
1
×
f
r
,
j
+
(
1
−
p
u
)
×
∑
j
=
i
+
1
m
f
r
,
j
)
+
f
r
,
i
(
p
u
∑
j
=
1
i
−
1
×
f
l
,
j
+
(
1
−
p
u
)
×
∑
j
=
i
+
1
m
f
l
,
j
)
f_{u,i}=f_{l,i}\times(p_u\sum\limits_{j=1}^{i-1}\times f_{r,j}+(1-p_u)\times \sum\limits_{j=i+1}^{m} f_{r,j})+f_{r,i}(p_u\sum\limits_{j=1}^{i-1}\times f_{l,j}+(1-p_u)\times \sum\limits_{j=i+1}^{m} f_{l,j})
fu,i=fl,i×(puj=1∑i−1×fr,j+(1−pu)×j=i+1∑mfr,j)+fr,i(puj=1∑i−1×fl,j+(1−pu)×j=i+1∑mfl,j)
比较显然地线段树合并了。
但是感觉实现有点困难。这里利用了线段树处理前缀和后缀的方法。
如果当前往左区间跳。就更新后缀。往右区间跳同理。
因为相当于是更新已经确定的值。线段树当前所在的区间都是还未确定的。
注意这是和,每次要乘上 p u p_u pu 或者 1 − p u 1-p_u 1−pu。
3.Code
#include<bits/stdc++.h>
#define int long long
#define ls (tr[rt].l)
#define rs (tr[rt].r)
#define LS (son[u][0])
#define RS (son[u][1])
using namespace std;
inline int rd(){
int s=0,w=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')w=-1;
for(;ch>='0'&&ch<='9';ch=getchar())s=s*10+ch-'0';
return s*w;
}
inline void wr(int x){
if(x<0)x=-x,putchar('-');
if(x>9)wr(x/10);
putchar(x%10+'0');
}
const int N=3e5+5,M=60;
const int P=998244353;
int n,cnt,tot,ans;
int id[N],son[N][2],sm[N];
int p[N],vl[N],h[N],f[N];
vector<int> a[N];
struct leafy_Tree{
int l,r;
int sum,laz;
}tr[N*M];
inline int qpow(int a,int b){
int sum=1;
while(b){
if(b&1)sum=(sum*a)%P;
a=(a*a)%P;
b>>=1;
}
return sum;
}
inline void New(int &rt){
rt=++tot;
tr[rt].laz=1;
}
inline void pushup(int rt){
tr[rt].sum=(tr[ls].sum+tr[rs].sum)%P;
}
inline void pushdown(int rt){
if(tr[rt].laz==1)return;
tr[ls].laz=(tr[ls].laz*tr[rt].laz)%P;
tr[rs].laz=(tr[rs].laz*tr[rt].laz)%P;
tr[ls].sum=(tr[ls].sum*tr[rt].laz)%P;
tr[rs].sum=(tr[rs].sum*tr[rt].laz)%P;
tr[rt].laz=1;
}
inline void ins(int &rt,int l,int r,int x){
if(!rt)New(rt);
if(l==r){
tr[rt].sum=1;return;
}
int mid=(l+r)>>1;
if(x<=mid)ins(tr[rt].l,l,mid,x);
else ins(tr[rt].r,mid+1,r,x);
pushup(rt);
}
inline int merge(int x,int y,int l,int r,int x_val,int y_val,int v){//好像保证了每个节点的权值户部相同。那么就不用考虑 l==r 的情况了。
if(!x&&!y)return 0;
if(!x){
tr[y].laz=(tr[y].laz*y_val)%P;
tr[y].sum=(tr[y].sum*y_val)%P;
return y;
}
if(!y){
tr[x].laz=(tr[x].laz*x_val)%P;
tr[x].sum=(tr[x].sum*x_val)%P;
return x;
}
pushdown(x);pushdown(y);
int mid=(l+r)>>1;
int c=0;New(c);
int RH=tr[tr[y].r].sum,RP=tr[tr[y].l].sum;
int LH=tr[tr[x].r].sum,LP=tr[tr[x].l].sum;
int A=(x_val+(P+1-v)*RH%P)%P,B=(y_val+(P+1-v)*LH)%P;
tr[c].l=merge(tr[x].l,tr[y].l,l,mid,A,B,v);
A=(x_val+v*RP%P)%P,B=(y_val+v*LP%P)%P;
tr[c].r=merge(tr[x].r,tr[y].r,mid+1,r,A,B,v);
pushup(c);
return c;
}
inline void dfs(int u){
if(!sm[u]){
ins(id[u],1,cnt,vl[u]);
return;
}
if(sm[u]==1)dfs(LS),id[u]=id[LS];
else dfs(LS),dfs(RS),id[u]=merge(id[LS],id[RS],1,cnt,0,0,p[u]);
}
inline void Get_ans(int rt,int l,int r){
if(l==r){
f[l]=tr[rt].sum;return;
}
pushdown(rt);
int mid=(l+r)>>1;
Get_ans(ls,l,mid);
Get_ans(rs,mid+1,r);
}
#undef int
int main(){
#define int long long
n=rd();
for(int i=1;i<=n;++i){
int x=rd();
if(i==1)continue;
son[x][sm[x]++]=i;
}
for(int i=1;i<=n;++i){
if(!sm[i]){
vl[i]=rd();h[++cnt]=vl[i];
}
else p[i]=rd(),p[i]=(p[i]*qpow(10000,P-2))%P;
}
sort(h+1,h+cnt+1);
for(int i=1;i<=n;++i){
if(!sm[i])vl[i]=lower_bound(h+1,h+cnt+1,vl[i])-h;
}
dfs(1);
Get_ans(id[1],1,cnt);
for(int i=1;i<=cnt;++i){
ans=(ans+i*h[i]%P*f[i]%P*f[i]%P)%P;
}
wr(ans);
return 0;
}