「PKUWC 2018」Minimax


傳送門:Here

一道線段樹合並好題

如果要維護點$ x$的信息,相當於合並$ x$的兩棵子樹

對於這題顯然有:任何葉子節點的權值都可能出現在其祖先上

因而我們只需要在線段樹合並的時候維護概率即可

我們令$ maxa(i)$表示在左子樹中權值比i大的概率,$ maxb(i)$表示在右子樹中權值比i大的概率,$ pL$表示這個節點選擇較大權值為最終結果的概率

若一個出現在左子樹的權值v成為了最終權值,概率應為$ maxb(v)*(1-pL)+(1-maxb(v))*pL)=maxb(v)+pL-2*maxb(v)*pL$

同理一個出現在右子樹的權值v成為最終權值的概率應為$ maxa(v)*(1-pL)+(1-maxa(v))*pL)=maxa(v)+pL-2*maxa(v)*pL$

直接暴力枚舉線段樹上所有節點更新權值效率低下,考慮如何在合並過程中完成$ maxa$和$ maxb$的維護

我們優先合並右子樹,即從大到小合並,初始可以認為不存在比自己大的也就是$ maxa=maxb=0$

合並兩棵樹的時候由於優先合並右邊,可以保證左邊均沒有被合並過而右邊已經合並完全

這時候$ maxa$和$ maxb$恰好就是對應的函數值,然后更新$ maxa$或$ maxb$的值(加上這段區間里出現的概率)

同普通線段樹合並,如果發現某棵子樹為空,則給另一顆子樹的所有概率乘上$ maxa$或$ maxb$,以標記形式下傳

最后遍歷根節點所對應的線段樹求出答案即可

 

code:

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 300010
#define rt register int
#define l putchar('\n')
#define ll long long
#define r read()
#define p 998244353
using namespace std;
inline ll read()
{
    register ll x = 0; char zf = 1; char ch;
    while (ch != '-' && !isdigit(ch)) ch = getchar();
    if (ch == '-') zf = -1, ch = getchar();
    while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar(); return x * zf;
}int i,j,k,m,n,x,y,z,cnt;
int fa[M],sum[M],son[M][2];
int pmax[M],v[M],Root[M],ys[M];
struct seg_ment{
    int L,R,ls,rs,gl,fla;
}a[20*M];
void insert(int &x,const int L,const int R,const int val)
{
    x=++cnt;a[x].L=L;a[x].R=R;a[x].gl=1;a[x].fla=1;
    if(L==R)return;
    const int mid=L+R>>1;
    if(val<=mid)insert(a[x].ls,L,mid,val);
    else insert(a[x].rs,mid+1,R,val);
}
int maxa,maxb;
void down(const int x)
{
    if(a[x].fla>1)
    {
        a[x].gl=(ll)a[x].gl*a[x].fla%p;
        a[a[x].ls].fla=(ll)a[a[x].ls].fla*a[x].fla%p;
        a[a[x].rs].fla=(ll)a[a[x].rs].fla*a[x].fla%p;
        a[x].fla=1;
    }
}
int merge(int x,int y,int pmax)//合並操作 
{
    if(!x&&!y)return 0;
    down(x);down(y);
    if(!x)
    {
        maxb=(maxb+a[y].gl)%p;//更新maxb的值 
        a[y].fla*=(maxa+pmax-2ll*maxa*pmax%p+p)%p;
        down(y);
        return y;
    }
    if(!y)
    {
        maxa=(maxa+a[x].gl)%p;//更新maxa的值 
        a[x].fla*=(maxb+pmax-2ll*maxb*pmax%p+p)%p;        
        down(x);    
        return x;
    }
    int d1=a[a[x].rs].gl,d2=a[a[y].rs].gl;
    a[x].rs=merge(a[x].rs,a[y].rs,pmax);
    a[x].ls=merge(a[x].ls,a[y].ls,pmax);
    a[x].gl=(a[a[x].ls].gl+a[a[x].rs].gl)%p;
    return x;
}

void dfs(const int x)//以dfs順序完成線段樹合並 
{        
    if(!sum[x])return;
    if(sum[x]==1)dfs(son[x][0]),Root[x]=Root[son[x][0]];
    if(sum[x]==2)
    {
        dfs(son[x][0]);
        dfs(son[x][1]);
        maxa=maxb=0;
        Root[x]=merge(Root[son[x][0]],Root[son[x][1]],pmax[x]);
    }    
}
ll ans=0;
void getans(const int x,const int L,const int R)//遍歷求答案 
{
    if(!a[x].gl)return;
    down(x);
    if(L==R)
    {
        cnt++;
        ans=(ans+(ll)cnt*ys[L]%p*a[x].gl%p*a[x].gl%p)%p;
        return;
    }
    const int mid=L+R>>1;
    getans(a[x].ls,L,mid);getans(a[x].rs,mid+1,R);
}
struct node{
    int x,id;
    bool operator <(const node s)const{
        return x<s.x;
    }
}Q[300010];int top;//離散化 
int main()
{
    n=read();
    for(rt i=1;i<=n;i++)
    {
        fa[i]=read();
        son[fa[i]][sum[fa[i]]++]=i;
    }
    for(rt i=1;i<=n;i++)
    if(sum[i])pmax[i]=r*796898467%p;//這是10000模998244353下逆元 
    else Q[++top]=(node){read(),i};
    sort(Q+1,Q+top+1);
    
    for(rt i=1;i<=top;i++)
    ys[i]=Q[i].x,insert(Root[Q[i].id],1,top,i);
    dfs(1);cnt=0;
    getans(Root[1],1,top);cout<<ans;
    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM