[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;
}
posted @ 2021-05-06 20:01  zkdxl  阅读(70)  评论(0编辑  收藏  举报