C70 线段树合并+概率论 P5298 [PKUWC2018] Minimax

视频链接: 256 线段树合并+概率论 P5298 [PKUWC2018] Minimax_哔哩哔哩_bilibili

 

 

 

 

Luogu P5298 [PKUWC2018] Minimax

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

void read(int &x){ //快读
  x=0; char c=getchar();
  while(!isdigit(c))c=getchar();
  while(isdigit(c))x=x*10+c-'0',c=getchar();
}
typedef long long LL;
const int N=300005, M=998244353;
#define mid (l+r)/2
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];
LL f[N*22],tag[N*22];
//p:叶子离散权值和非叶子概率, v:叶子权值, d:根取值的概率
//f:节点权值的概率和, tag:乘法懒标记

int qpow(int x,int n){ //快速幂
  int s=1;
  for(;n;n>>=1,x=1ll*x*x%M)
    if(n&1) s=1ll*s*x%M;
  return s;
}
void update(int x,int v){ //更新x点信息
  f[x]=f[x]*v%M; tag[x]=tag[x]*v%M;
}
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,LL p){ //合并
  //px,py:x,y的前缀和,sx,sy:x,y的后缀和,p:x与y的父节点的概率
  if(!x&&!y) return 0;
  //若x不空,则f[x]*(p*y的前缀和+(1-p)*y的后缀和)
  if(!y){update(x,(p*py%M+(1-p+M)*sy%M)%M);return x;}
  if(!x){update(y,(p*px%M+(1-p+M)*sx%M)%M);return y;}
  pushdown(x),pushdown(y);
  LL lx=f[ls[x]],ly=f[ls[y]],rx=f[rs[x]],ry=f[rs[y]];
  //x,y同步走左分支时,向下累计各自的后缀和
  ls[x]=merge(ls[x],ls[y],px,py,(sx+rx)%M,(sy+ry)%M,p);
  rs[x]=merge(rs[x],rs[y],(px+lx)%M,(py+ly)%M,sx,sy,p);
  f[x]=(f[ls[x]]+f[rs[x]])%M; return x;
}
void dfs(int x){ //递归原树
  if(!ch[x][0])  //x是叶子,创建线段树
    {change(root[x],1,m,p[x]);return;}
  if(!ch[x][1])  //x只有左儿子,继承线段树
    {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);
}
int main(){
  read(n);
  for(int i=1; i<=n; i++) read(fa[i]);
  for(int i=1; i<=n; i++) read(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,M-2)%M; //非叶子概率
  sort(v+1,v+m+1);
  for(int i=1; i<=n; i++) //叶子的离散权值
    if(!cnt[i]) p[i]=lower_bound(v+1,v+m+1,p[i])-v;
  dfs(1);
  dfs2(root[1],1,m);
  for(int i=1; i<=m; i++)
    ans=(ans+1ll*i*v[i]%M*d[i]%M*d[i]%M)%M;
  printf("%d\n",ans);
}

 

posted @ 2023-11-15 22:41  董晓  阅读(176)  评论(0编辑  收藏  举报