题意
给了你一棵二叉树,一号节点时根节点,每个叶子节点都有一个权值,非叶子节点的权值有 P P P的概率是它子节点的最大值,有 1 − P 1-P 1−P的几率是它子节点的最小值,求一号节点取所有权值的概率,叶子节点的权值保证不同,且 P P P换算成分数后 0 < P < 1 0<P<1 0<P<1,可以证明一号节点能取完所有叶子节点的权值;
题解
看起来非常像树形DP,尝试推一下转移式子,先离散化权值,定义
F
[
u
]
[
i
]
F[u][i]
F[u][i]表示
u
u
u节点取权值
i
i
i的概率;叶子节点肯定只有一个
i
i
i,且概率为
1
1
1;考虑怎么转移,如果只有一个儿子,父亲的suoy取值概率肯定直接等于儿子的所有取值概率;现在有两个儿子,如果权值
i
i
i来自左儿子
s
o
n
[
L
]
son[L]
son[L],那么
F
[
u
]
[
i
]
=
F
[
s
o
n
[
L
]
[
i
]
∗
(
∑
j
,
j
<
i
F
[
s
o
n
[
R
]
]
[
j
]
∗
P
u
+
∑
j
,
j
>
i
F
[
s
o
n
[
R
]
]
[
j
]
∗
(
1
−
P
u
)
)
F[u][i]=F[son[L][i]*(\sum_{j,j<i}F[son[R]][j]*P_u+\sum_{j,j>i}F[son[R]][j]*(1-P_u))
F[u][i]=F[son[L][i]∗(j,j<i∑F[son[R]][j]∗Pu+j,j>i∑F[son[R]][j]∗(1−Pu))
从右儿子的转移同理,现在考虑要维护哪些东西才能维护这个式子,首先肯定要知道每个点有哪些取值及这些取值的概率,能快速求出兄弟节点中所有小于和大于某权值的和,想到平衡树或者动态开点线段树;但是怎么维护转移呢,考虑启发式合并,在启发式合并时记录兄弟节点中大于某段区间的概率和(小于的用
1
1
1减去大于的就能得到了),那么在启发式合并的最后取节点时再把贡献算进去,然后标记下传就行了;
#include<bits/stdc++.h>
#define Fst first
#define Snd second
#define RG register
#define mp make_pair
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long LL;
typedef long double LD;
typedef unsigned int UI;
typedef unsigned long long ULL;
template<typename T> inline void read(T& x) {
char c = getchar();
bool f = false;
for (x = 0; !isdigit(c); c = getchar()) {
if (c == '-') {
f = true;
}
}
for (; isdigit(c); c = getchar()) {
x = x * 10 + c - '0';
}
if (f) {
x = -x;
}
}
template<typename T, typename... U> inline void read(T& x, U& ... y) {
read(x), read(y...);
}
const int N=3e5+10,P=998244353;
int n,p,cnt,size;
int G[N],V[N],head[N],root[N];
bool YES[N];
map<int,int> S;
struct Node {
int sum,lazy;
int lo,ro;
}Tr[N*20];
#define L Tr[o].lo
#define R Tr[o].ro
struct SegmentTree {
void Modify(int l,int r,int &o,int pos) {
o=++size;
Tr[o].sum=1; Tr[o].lazy=1;
if(l==r) return;
int mid=l+r>>1;
if(pos<=mid) Modify(l,mid,L,pos);
else Modify(mid+1,r,R,pos);
}
void Down(int o,int val) {
if(!o) return;
Tr[o].sum=1ll*Tr[o].sum*val%P;
Tr[o].lazy=1ll*Tr[o].lazy*val%P;
}
void Pushdown(int o) {
if(Tr[o].lazy!=1) {
Down(L,Tr[o].lazy);
Down(R,Tr[o].lazy);
Tr[o].lazy=1;
}
}
void Merge(int l,int r,int &x,int y,int a,int b,int p) {
if(!x||!y) {
if(!x) Down(y,(1ll*(1-b+P)%P*p%P+1ll*(1-p+P)%P*b%P)%P);
else Down(x,(1ll*(1-a+P)%P*p%P+1ll*(1-p+P)%P*a%P)%P);
x=x+y;
return;
}
Pushdown(x); Pushdown(y);
int mid=l+r>>1;
Merge(l,mid,Tr[x].lo,Tr[y].lo,(a+Tr[Tr[y].ro].sum)%P,(b+Tr[Tr[x].ro].sum)%P,p); Merge(mid+1,r,Tr[x].ro,Tr[y].ro,a,b,p);
Tr[x].sum=Tr[Tr[x].lo].sum+Tr[Tr[x].ro].sum;
if(Tr[x].sum>=P) Tr[x].sum-=P;
}
int Query(int l,int r,int o,int pos) {
if(l==r) return Tr[o].sum;
Pushdown(o);
int mid=l+r>>1,t;
if(pos<=mid) t=Query(l,mid,L,pos);
else t=Query(mid+1,r,R,pos);
Tr[o].sum=Tr[L].sum+Tr[R].sum;
if(Tr[o].sum>=P) Tr[o].sum-=P;
return t;
}
}SgT;
struct Edge {
int to,last;
Edge () {}
Edge (int a,int b) :to(a),last(b) {}
}edge[N];
void ADD(int a,int b) {
edge[++p]=Edge(b,head[a]); head[a]=p;
}
void DFS(int u) {
if(!YES[u]) {
SgT.Modify(1,cnt,root[u],S[G[u]]);
return;
}
for(int i=head[u];i;i=edge[i].last) {
int v=edge[i].to;
DFS(v);
if(!root[u]) root[u]=root[v];
else SgT.Merge(1,cnt,root[u],root[v],0,0,G[u]);
}
}
int Pow(int a,int k) {
int res=1;
for(int i=k;i;i>>=1) {
if(i&1) res=1ll*res*a%P;
a=1ll*a*a%P;
}
return res;
}
int main() {
#ifdef rua
freopen("GG.in","r",stdin);
#endif
read(n);
for(int i=1;i<=n;++i) {
int fa; read(fa);
if(fa) ADD(fa,i),YES[fa]=true;
}
int Inv=Pow(10000,P-2);
for(int i=1;i<=n;++i) {
read(G[i]);
if(!YES[i]) V[++cnt]=G[i];
else G[i]=1ll*G[i]*Inv%P;
}
sort(V+1,V+cnt+1);
for(int i=1;i<=cnt;++i) S[V[i]]=i;
DFS(1);
int res=0;
for(int i=1;i<=cnt;++i) {
int t=SgT.Query(1,cnt,root[1],i);
(res+=1ll*i*V[i]%P*t%P*t%P)%=P;
}
printf("%d\n",res);
return 0;
}