[PKUWC2018] Minimax
\(\text{Problem}:\)[PKUWC2018] Minimax
\(\text{Solution}:\)
首先对权值进行离散化。设 \(f_{i,j}\) 表示结点 \(i\) 的权值离散化后为 \(j\) 的概率。令 \(x,y\) 分别表示 \(i\) 的左右儿子,由于叶子结点权值互不相同,有:
\[f_{i,j}=f_{x,j}\times(p_{i}\sum\limits_{k=1}^{j}f_{y,k}+(1-p_{i})\sum\limits_{k=j}^{m}f_{y,k})+f_{y,j}\times (p_{i}\sum\limits_{k=1}^{j}f_{x,k}+(1-p_{i})\sum\limits_{k=j}^{m}f_{x,k})
\]
发现有前缀和后缀的形式,故再设 \(g_{i,j}\) 表示结点 \(i\) 的权值离散化后小于等于 \(j\) 的概率,\(h_{i,j}\) 表示结点 \(i\) 的权值离散化后大于等于 \(j\) 的概率,有:
\[f_{i,j}=p_{i}(f_{x,j}\times g_{y,j}+f_{y,j}\times g_{x,j})+(1-p_{i})(f_{x,j}\times h_{y,j}+f_{y,j}\times h_{x,j})
\]
由于叶子结点 \(f\) 只在一个位置有信息,故考虑线段树合并,在线段树上维护区间和。
设现在要合并结点 \(x\) 和 \(y\),首先记录没有合并前 \(x,y\) 左右子树的区间和。递归合并左子树时,将原先右子树的贡献加入;合并右子树同理,这样就处理了前缀和与后缀和对答案的贡献。在叶子结点做区间乘法即可。
总时间复杂度 \(O(n\log n)\)。
\(\text{Code}:\)
#include <bits/stdc++.h>
#pragma GCC optimize(3)
//#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
#define vi vector<int>
#define vpi vector<pair<int,int>>
using namespace std; const int N=300010, Mod=998244353;
inline int read()
{
int s=0, w=1; ri char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
return s*w;
}
int n,a[N],ta[N],m;
int head[N],maxE; struct Edge { int nxt,to; }e[N];
inline void Add(int u,int v) { e[++maxE].nxt=head[u]; head[u]=maxE; e[maxE].to=v; }
inline int ksc(int x,int p) { int res=1; for(;p;p>>=1, x=1ll*x*x%Mod) if(p&1) res=1ll*res*x%Mod; return res; }
struct Tree { int lc,rc,sum,tag; }D[N*20]; int root[N],cnt;
void Build(int &x,int l,int r,int pos)
{
if(!x) x=++cnt;
D[x].sum++, D[x].tag=1;
if(l==r) return;
int mid=(l+r)/2;
if(pos<=mid) Build(D[x].lc,l,mid,pos);
else Build(D[x].rc,mid+1,r,pos);
}
inline void ps(int x) { D[x].sum=(D[D[x].lc].sum+D[D[x].rc].sum)%Mod; }
inline void Change(int x,int k)
{
D[x].tag=1ll*D[x].tag*k%Mod;
D[x].sum=1ll*D[x].sum*k%Mod;
}
inline void pd(int x)
{
if(D[x].tag!=1)
{
int k=D[x].tag;
Change(D[x].lc,k);
Change(D[x].rc,k);
D[x].tag=1;
}
}
int Merge(int x,int y,int l,int r,int sx,int sy,int P)
{
if(!x&&!y) return 0;
if(!x) { Change(y,sy); return y; }
if(!y) { Change(x,sx); return x; }
pd(x), pd(y);
int lx=D[D[x].lc].sum, ly=D[D[y].lc].sum, rx=D[D[x].rc].sum, ry=D[D[y].rc].sum, mid=(l+r)/2;
D[x].lc=Merge(D[x].lc,D[y].lc,l,mid,(sx+1ll*ry*(Mod+1-P)%Mod)%Mod,(sy+1ll*rx*(Mod+1-P)%Mod)%Mod,P);
D[x].rc=Merge(D[x].rc,D[y].rc,mid+1,r,(sx+1ll*ly*P%Mod)%Mod,(sy+1ll*lx*P%Mod)%Mod,P);
ps(x);
return x;
}
void DFS(int x)
{
if(!head[x]) { Build(root[x],1,m,a[x]); return; }
for(ri int i=head[x];i;i=e[i].nxt)
{
int v=e[i].to;
DFS(v);
if(!root[x]) root[x]=root[v];
else root[x]=Merge(root[x],root[v],1,m,0,0,a[x]);
}
}
int ans[N];
void Get(int x,int l,int r)
{
if(!x) return;
if(l==r) { ans[l]=D[x].sum; return; }
pd(x);
int mid=(l+r)/2;
Get(D[x].lc,l,mid);
Get(D[x].rc,mid+1,r);
}
signed main()
{
n=read();
for(ri int i=1;i<=n;i++)
{
int x=read();
if(x) Add(x,i);
}
for(ri int i=1,inv=ksc(10000,Mod-2);i<=n;i++)
{
a[i]=read();
if(head[i]) a[i]=1ll*a[i]*inv%Mod;
else ta[++m]=a[i];
}
sort(ta+1,ta+1+m);
for(ri int i=1;i<=n;i++) if(!head[i]) a[i]=lower_bound(ta+1,ta+1+m,a[i])-ta;
DFS(1);
Get(root[1],1,m);
int res=0;
for(ri int i=1;i<=m;i++) res=(res+1ll*i*ta[i]%Mod*ans[i]%Mod*ans[i]%Mod)%Mod;
printf("%d\n",res);
return 0;
}
夜畔流离回,暗叹永无殿。
独隐万花翠,空寂亦难迁。
千秋孰能为,明灭常久见。
但得心未碎,踏遍九重天。