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); }