「PKUWC2018」Minimax [线段树合并 概率/期望]

「PKUWC2018」Minimax

Tags:线段树合并 概率DP


「PKUWC2018」Minimax

题意

有一个有n个结点的有根二叉树,对于一个点x。
如果x为叶子结点,那么权值为它本身。
如果x非叶子结点那么其权值有p的可能是子结点的最大值,有1-p的可能是子结点权值的最小值。
对于结点1的权值有m中可能,那么设权值第i小的值为V[i],概率为D[i],那么求

i=1miV[i]D[i]2 ∑ i = 1 m i ∗ V [ i ] ∗ D [ i ] 2

分析

现在仔细一看感觉好像并不是特别难的样子啊…
合并两边的点,有p的概率取较大值,有1-p的概率取较小值。
记f[i]为左边子结点取到权值为i的概率。
记g[i]为右边子结点取到权值为i的概率。

左子结点,值为i,被取的概率就是

f[i](p(j=1ig[i])+(1p)(j=i+1mg[i])) f [ i ] ∗ ( p ∗ ( ∑ j = 1 i g [ i ] ) + ( 1 − p ) ∗ ( ∑ j = i + 1 m g [ i ] ) )

右子结点,值为i,被取的概率就是
g[i](p(j=1if[i])+(1p)(j=i+1mf[i])) g [ i ] ∗ ( p ∗ ( ∑ j = 1 i f [ i ] ) + ( 1 − p ) ∗ ( ∑ j = i + 1 m f [ i ] ) )

然后这样就可以用归并排序瞎搞然后就是50pts啦。当时考场上写出来的还蛮得意2333

疑似线段树合并?

f[i](p(j=1ig[i])+(1p)(1j=1ig[i])) f [ i ] ∗ ( p ∗ ( ∑ j = 1 i g [ i ] ) + ( 1 − p ) ∗ ( 1 − ∑ j = 1 i g [ i ] ) )

然后因为所有权值都不相同所以说直接线段树合并就好了?合并到l=r的时候一定只有一棵线段树是有值的。然后用只需要在过程中记录两边前缀和就好了。

code
#include<bits/stdc++.h>
using namespace std;
#define M 300005
#define K 20
#define mo 998244353
#define ll long long 
ll res[M];
void read(int &x){
    x=0; char c=getchar();
    for (;c<48;c=getchar());
    for (;c>47;c=getchar())x=(x<<1)+(x<<3)+(c^48);
}
void read(ll &x){
    x=0; char c=getchar();
    for (;c<48;c=getchar());
    for (;c>47;c=getchar())x=(x<<1)+(x<<3)+(c^48);
}
void Add(ll &x,ll y){
    x=(x+y)%mo;
}
struct Marisa{
    ll laz[M*K],f[M*K];
    int ls[M*K],rs[M*K],tot;
    void down(int p){
        if (p){
            if (laz[p]!=1){
                if (ls[p]){
                    laz[ls[p]]=laz[ls[p]]*laz[p]%mo;
                    f[ls[p]]=f[ls[p]]*laz[p]%mo;
                }
                if (rs[p]){
                    laz[rs[p]]=laz[rs[p]]*laz[p]%mo;
                    f[rs[p]]=f[rs[p]]*laz[p]%mo;
                }
                laz[p]=1;
            }
        }
    }
    void build(int l,int r,int x,int &p){
        p=++tot;
        f[p]=1; laz[p]=1;
        if (l==r)return ;       
        int mid=(l+r)>>1;
        if (x<=mid)build(l,mid,x,ls[p]);
        else build(mid+1,r,x,rs[p]);    
    }   
    int uni(int l,int r,ll val,ll suml,ll sumr,int lp,int rp){
        down(lp); down(rp);
        if (lp==0){
            laz[rp]=laz[rp]*(val*suml%mo+(mo+1-val)*(mo+1-suml)%mo)%mo;
            f[rp]=f[rp]*(val*suml%mo+(mo+1-val)*(mo+1-suml)%mo)%mo;
            return rp;
        }
        if (rp==0){
            laz[lp]=laz[lp]*(val*sumr%mo+(mo+1-val)*(mo+1-sumr)%mo)%mo;
            f[lp]=f[lp]*(val*sumr%mo+(mo+1-val)*(mo+1-sumr)%mo)%mo;
            return lp;
        }
        int p=++tot,mid=(l+r)>>1;
        rs[p]=uni(mid+1,r,val,(suml+f[ls[lp]])%mo,(sumr+f[ls[rp]])%mo,rs[lp],rs[rp]);
        ls[p]=uni(l,mid,val,suml,sumr,ls[lp],ls[rp]);
        f[p]=(f[ls[p]]+f[rs[p]])%mo; laz[p]=1;
        return p;
    }   
    void pt(int l,int r,int p){ 
        if (l==r){
            res[l]=f[p];
            return;
        }
        down(p);
        int mid=(l+r)>>1;
        pt(l,mid,ls[p]);
        pt(mid+1,r,rs[p]);
    }
}T;
int deg[M],son[M][2],rt[M],tot,a[M];
ll p[M];
void dfs(int x){
    if (!deg[x]){
        T.build(1,tot,p[x],rt[x]);
        return ;
    }   
    int i;
    for (i=0;i<deg[x];i++){
        dfs(son[x][i]);
    }
    if (deg[x]==1){
        rt[x]=rt[son[x][0]];
    }
    else{
        rt[x]=T.uni(1,tot,p[x],0,0,rt[son[x][0]],rt[son[x][1]]);
    }
}
ll ksm(ll a,ll b){
    ll res=1;
    for (;b;b>>=1){
        if (b&1)res=res*a%mo;
        a=a*a%mo;
    }
    return res;
}
int ch(int l,int r,int x){
    int mid;
    for (;;){
        mid=(l+r)>>1;
        if (a[mid]==x)return mid;
        if (a[mid]<x)l=mid+1;else r=mid-1;
    }
}
int main(){
//  freopen("1.in","r",stdin);
    int n,i,f;
    read(n); ll chu=ksm(10000,mo-2);
    for (i=1;i<=n;i++){
        read(f);
        son[f][deg[f]++]=i;
    }
    for (i=1;i<=n;i++){
        read(p[i]);
        if (deg[i]){
            p[i]=(chu*p[i])%mo;
        }
        else{
            a[++tot]=p[i];
        }
    }
    sort(a+1,a+tot+1);
    tot=unique(a+1,a+tot+1)-a-1;
    for (i=1;i<=n;i++)if (!deg[i]){
        p[i]=ch(1,tot,p[i]);
    }
    dfs(1);
    T.pt(1,tot,rt[1]);
    ll ans=0;
    for (i=1;i<=tot;i++){
        res[i]=(res[i]%mo+mo)%mo;
        Add(ans,res[i]*a[i]%mo*i%mo*res[i]%mo);
    }
    printf("%lld\n",ans);
    return 0;
}
AC之后的碎碎念

然而感觉好像挺容易l和r打反死活调不出来啊…超绝望的感觉明明写的都是对的。
然而大概以后写这样子的线段树也树需要注意这样的东西…
什么lsum,rsum,ls,rs,lp,rp的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值