#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int long long
typedef unsigned long long ull;
typedef pair<ll,ll> pii;
const int inf=0x3f3f3f3f;
const int N=4e5+10;
const int mod=998244353;
const ll INF=2e9+10;
#define mid ((l+r)>>1)
int n,m,ans,tot;
int fa[N],ch[N][2],cnt[N],p[N],v[N],d[N];
int root[N],ls[N*22],rs[N*22];
int f[N*22],tag[N*22];
//v是叶子权值,d是根取值的概率
//f节点所以可能权值的概率和 ,tag乘法懒标记
int qpow(int a,int b){
int res=1;
while(b){
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
void update(int x,int v){
f[x]=f[x]*v%mod;
tag[x]=tag[x]*v%mod;
}
void pushdown(int x){
if(tag[x]>1)
update(ls[x],tag[x]),update(rs[x],tag[x]),tag[x]=1;
}
void change(int &x,int l,int r,int p){
if(!x){
x=++tot;tag[x]=1;
}
f[x]++;
if(l==r) return;
if(p<=mid) change(ls[x],l,mid,p);
else change(rs[x],mid+1,r,p);
}
int merge(int x,int y,int px,int py,int sx,int sy,int p){
if(!x&&!y) return 0;
//因为数据保证每个叶子权值都是不一样的,也就是说子树的每个数都只能在左子树或者右子树
if(!y){//出现在x的时候y就不可能有这个数,让fx乘上(p*y的前缀+(1-p)*y的后缀)
update(x,(p*py%mod+(1-p+mod)*sy%mod)%mod);
return x;
}
if(!x){
update(y,(p*px%mod+(1-p+mod)*sx%mod)%mod);
return y;
}
//比如合并2和3的时候,2子树表示的【1,2】区间在3子树没有
/* 1 [1,4] 所以不会递归合并到4,5,在2的时候就结束了
/ \ 这时候是对区间【1,2】进行区间乘操作
[1,2] 2 3 [3,4] 所以需要tag数组和下传tag数组
/ \ / \
4 5 6 7
*/
pushdown(x),pushdown(y);
int lx=f[ls[x]],ly=f[ls[y]],rx=f[rs[x]],ry=f[rs[y]];
//因为fx是记录x节点代表区间的总概率和
//lx记录x子树当前区间的左半边的概率总和
//rx记录x子树当前区间的右半边的概率总和
//合并左区间的时候,右半区间概率和就是左半区间所要的后缀和
//合并右区间的时候,左半区间概率和就是右半区间所需的前缀和
//所以进去左区间的时候前缀和不能修改,只需要加上后缀和
//因为对于我们要找的x节点来说,我们会往x节点去跑,
//前缀会不断加上左边比x小的概率和,后缀也会不断加上右边比x大的概率和
ls[x]=merge(ls[x],ls[y],px,py,(sx+rx)%mod,(sy+ry)%mod,p);
rs[x]=merge(rs[x],rs[y],(px+lx)%mod,(py+ly)%mod,sx,sy,p);
f[x]=(f[ls[x]]+f[rs[x]])%mod;
return x;
}
void dfs(int x){
if(!ch[x][0]){//没有儿子就是叶子节点,创建线段树
change(root[x],1,m,p[x]);
return;
}
if(!ch[x][1]){//只有一个儿子,创建完儿子直接继承
dfs(ch[x][0]);
root[x]=root[ch[x][0]];
return;
}
dfs(ch[x][0]),dfs(ch[x][1]);
root[x]=merge(root[ch[x][0]],root[ch[x][1]],0,0,0,0,p[x]);
}
void dfs2(int x,int l,int r){
if(l==r){
d[l]=f[x];return;
}
pushdown(x);
dfs2(ls[x],l,mid);
dfs2(rs[x],mid+1,r);
}
void solve(){
cin>>n;
for(int i=1;i<=n;i++)
cin>>fa[i];
for(int i=1;i<=n;i++)
cin>>p[i];
for(int i=1;i<=n;i++)
ch[fa[i]][cnt[fa[i]]++]=i;
for(int i=1;i<=n;i++){
if(!cnt[i]) v[++m]=p[i];
else p[i]=1ll*p[i]*qpow(10000,mod-2)%mod;
}
sort(v+1,v+1+m);
for(int i=1;i<=n;i++)
if(!cnt[i])
p[i]=lower_bound(v+1,v+1+m,p[i])-v;
dfs(1);
//可能有的点还没pushdown,还得遍历一次
dfs2(root[1],1,m);
for(int i=1;i<=m;i++)
ans=(ans+1ll*i*v[i]%mod*d[i]%mod*d[i]%mod)%mod;
cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
while(t--){
solve();
}
return 0;
}
minimax维护前后缀概率和
最新推荐文章于 2024-08-09 19:02:56 发布