[PKUWC2018] Minimax 题解

文章讲述了如何使用动态规划和线段树技术解决一个关于二叉树中节点权值概率问题的Minimax题,通过离散化权值并计算出现概率,利用线段树结构高效处理前缀和后缀概率更新。
摘要由CSDN通过智能技术生成

[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 1px 的概率是它的子节点的权值的最小值。

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=1miViDi2

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=1i1×fr,j+(1pu)×j=i+1mfr,j)+fr,i(puj=1i1×fl,j+(1pu)×j=i+1mfl,j)

比较显然地线段树合并了。

但是感觉实现有点困难。这里利用了线段树处理前缀和后缀的方法。

如果当前往左区间跳。就更新后缀。往右区间跳同理。

因为相当于是更新已经确定的值。线段树当前所在的区间都是还未确定的。

注意这是和,每次要乘上 p u p_u pu 或者 1 − p u 1-p_u 1pu

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;
}
  • 35
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值