zoukankan      html  css  js  c++  java
  • CF482E ELCA

    ELCA

    给一棵(n)个点以(1)为根的树,每个点有权值(s_i),支持两种操作:

    1. (x)及其子树连到(y)上。

    2. (s_x=v)

    每次操作后都询问expected value written on the lowest common ancestor of two equiprobably selected vertices (i) and (j). Please note that the vertices (i) and (j) can be the same (in this case their lowest common ancestor coincides with them).

    (nleq 5 imes 10^4)

    题解

    没人看得懂的题解:https://www.cnblogs.com/suncongbo/p/11330695.html

    表面上是期望问题,实际上是计数问题。统计节点(i)作为LCA的次数即可。

    [E=frac{1}{n^2}sum_{i=1}^ns_ileft(1+2( ext{siz}_i-1)+sum_{p_j=i}sum_{p_k=i,k eq j} ext{siz}_j ext{siz}_k ight) ]

    注意到

    [sum_{p_j=i}sum_{p_k=i,k eq j} ext{siz}_j ext{siz}_k =left(sum_{p_j=i} ext{siz}_j ight)^2-sum_{p_j=i} ext{siz}_j^2 =( ext{siz}_i-1)^2-sum_{p_j=i} ext{siz}_j^2 ]

    所以有

    [E=frac{1}{n^2}sum_{i=1}^ns_ileft( ext{siz}_i^2-sum_{p_j=i} ext{siz}_j^2 ight) ]

    所以对树(T)我们需要维护

    [egin{gather} A_T=sum_{iin T}s_i ext{siz}_i^2\ B_T=sum_{iin T}s_isum_{p_j=i} ext{siz}_j^2\ end{gather} ]

    LCT维护子树信息的时候,Splay里面要按照中序遍历所确定的链来维护。我一稿居然按照Splay的二叉树关系维护了……

    考虑push_up的时候发生的事。带imag_前缀的表示虚子树的信息。

    1. 首先是( ext{siz}_{T(x)})的维护。(T(x))代表(x)在Splay里的子树的中序遍历所确定的实链加上它们的虚子树构成的树。

      [ ext{siz}_{T(x)}= ext{siz}_{T( ext{rc})}+ ext{imag_siz}_x+1+ ext{siz}_{T( ext{lc})} ]

    2. 其次是(A_{T(x)})的维护。(x)自己的贡献和(x)右子树的贡献比较好算,但是(x)在Splay中左子树里的节点(在实链上的)就不一样了,因为它们的子树大小都发生了变化。考虑变化量,(s_i( ext{siz}_i+Delta)^2-s_i ext{siz}_i^2=s_iDelta^2+2s_i ext{siz}_iDelta),所以我们还要维护Splay链上(不包含虚子树)的(s_x)(s_x ext{siz}_x)的和。对实链(L)

      [egin{gather} ext{sum_s}_L=sum_{iin L}s_i\ A'_L=sum_{iin L}s_i ext{siz}_i end{gather} ]

      (Delta= ext{siz}_{T( ext{rc})}+1+ ext{imag_siz}_x),那么有

      [egin{gather} ext{sum_s}_{L(x)}= ext{sum_s}_{L( ext{rc})}+s_x+ ext{sum_s}_{L( ext{lc})}\ A'_{L(x)}=A'_{L( ext{rc})}+s_xDelta+A'_{L( ext{lc})}+ ext{sum_s}_{L( ext{lc})}Delta\ A_{T(x)}=A_{T( ext{rc})}+ ext{imag_A}_x+s_xDelta^2+A_{T( ext{lc})}+2A'_{L( ext{lc})}Delta+ ext{sum_s}_{L( ext{lc})}Delta^2 end{gather} ]

    3. 最后是(B_{T(x)})的维护。同样考虑(x)在Splay中左子树里的节点,它们的变化量要诡异一点,不过跟(A)大同小异。记( ext{son}_i)表示节点(i)在实链剖分中的重儿子,

      [egin{gather} B'_L=sum_{iin L}s_i ext{siz}_{ ext{son}_i} end{gather} ]

      那么有

      [egin{gather} B'_{L(x)}=B'_{L( ext{rc})}+s_x ext{siz}_{T( ext{rc})}+B'_{L( ext{lc})}+ ext{sum_s}_{T( ext{lc})}Delta\ B_{T(x)}=B_{T( ext{rc})}+ ext{imag_B}_x+s_x( ext{imag_sq_siz}_x+ ext{siz}_{T( ext{rc})}^2)+B_{T( ext{lc})}+2B'_{L( ext{lc})}Delta+ ext{sum_s}_{L( ext{lc})}Delta^2 end{gather} ]

    当然了,为了支持reverse操作,我们还需要对(A,B,A',B')维护一个合并方向相反的值。

    维护好这些,易得答案( ext{ans}=frac{1}{n^2}(A_{T( ext{root})}-B_{T( ext{root})}))

    你已经完全会了,快点开始写bug吧!

    时间复杂度(O(nlog n)),但常数估计都抵一个log了。

    #include<bits/stdc++.h>
    using namespace std;
    using int64=long long;
    
    template<class T>
    T read(){
        T x=0;char w=1,c=getchar();
        for(;!isdigit(c);c=getchar())if(c=='-') w=-w;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        return x*w;
    }
    template<class T>
    T& read(T& x){
        return x=read<T>();
    }
    
    constexpr int N=5e4+10;
    int ch[N][2],fa[N],rev[N];
    int64 siz[N],imag_siz[N],imag_sq_siz[N];
    int64 s[N],sum_s[N];
    int64 A[N][2],A1[N][2],imag_A[N]; // A1 -> A'
    int64 B[N][2],B1[N][2],imag_B[N]; // B1 -> B'
    
    #define lc ch[x][0]
    #define rc ch[x][1]
    // helper function
    bool nroot(int x){
        return ch[fa[x]][0]==x || ch[fa[x]][1]==x;
    }
    void push_up(int x){
        siz[x]=siz[rc]+imag_siz[x]+1+siz[lc];
        sum_s[x]=sum_s[rc]+s[x]+sum_s[lc];
        // right -> left
        int64 D=siz[rc]+1+imag_siz[x];
        A1[x][0]=A1[rc][0]+s[x]*D+A1[lc][0]+sum_s[lc]*D;
        A[x][0]=A[rc][0]+imag_A[x]+s[x]*D*D+A[lc][0]+2*A1[lc][0]*D+sum_s[lc]*D*D;
        B1[x][0]=B1[rc][0]+s[x]*siz[rc]+B1[lc][0]+sum_s[lc]*D;
        B[x][0]=B[rc][0]+imag_B[x]+s[x]*(imag_sq_siz[x]+siz[rc]*siz[rc])+B[lc][0]+2*B1[lc][0]*D+sum_s[lc]*D*D;
        // left -> right
        D=siz[lc]+1+imag_siz[x];
        A1[x][1]=A1[lc][1]+s[x]*D+A1[rc][1]+sum_s[rc]*D;
        A[x][1]=A[lc][1]+imag_A[x]+s[x]*D*D+A[rc][1]+2*A1[rc][1]*D+sum_s[rc]*D*D;
        B1[x][1]=B1[lc][1]+s[x]*siz[lc]+B1[rc][1]+sum_s[rc]*D;
        B[x][1]=B[lc][1]+imag_B[x]+s[x]*(imag_sq_siz[x]+siz[lc]*siz[lc])+B[rc][1]+2*B1[rc][1]*D+sum_s[rc]*D*D;
    }
    void reverse(int x){
        swap(A1[x][0],A1[x][1]);
        swap(A[x][0],A[x][1]);
        swap(B1[x][0],B1[x][1]);
        swap(B[x][0],B[x][1]);
        swap(ch[x][0],ch[x][1]);
        rev[x]^=1;
    }
    void push_down(int x){
        if(rev[x]){
            if(ch[x][0]) reverse(ch[x][0]);
            if(ch[x][1]) reverse(ch[x][1]);
            rev[x]=0;
        }
    }
    // basic operation
    void rotate(int x){
        int y=fa[x],z=fa[y],l=x==ch[y][1],r=l^1;
        if(nroot(y)) {ch[z][y==ch[z][1]]=x;} fa[x]=z;
        ch[y][l]=ch[x][r],fa[ch[x][r]]=y;
        ch[x][r]=y,fa[y]=x;
        push_up(y);
    }
    void update(int x){
        if(nroot(x)) update(fa[x]);
        push_down(x);
    }
    void splay(int x){
        update(x);
        for(;nroot(x);rotate(x)){
            int y=fa[x],z=fa[y];
            if(nroot(y)) rotate((x==ch[y][1])!=(y==ch[z][1])?x:y);
        }
        push_up(x);
    }
    void access(int x){
        for(int y=0;x;y=x,x=fa[x]){
            splay(x);
            imag_siz[x]+=siz[ch[x][1]]-siz[y];
            imag_sq_siz[x]+=siz[ch[x][1]]*siz[ch[x][1]]-siz[y]*siz[y];
            imag_A[x]+=A[ch[x][1]][0]-A[y][0];
            imag_B[x]+=B[ch[x][1]][0]-B[y][0];
            ch[x][1]=y;
            push_up(x);
        }
    }
    // advanced operation
    void make_root(int x){
        access(x),splay(x),reverse(x);
    }
    int find_root(int x){
        access(x),splay(x);
        while(ch[x][0]) x=ch[x][0];
        splay(x);
        return x;
    }
    void split(int x,int y){
        make_root(x),access(y),splay(y);
    }
    void link(int x,int y){
        make_root(x),access(y),splay(y);
        imag_siz[y]+=siz[x];
        imag_sq_siz[y]+=siz[x]*siz[x];
        imag_A[y]+=A[x][0];
        imag_B[y]+=B[x][0];
        fa[x]=y;
        push_up(y);
    }
    void cut(int x,int y){
        split(x,y);
        fa[x]=ch[y][0]=0;
        push_up(y);
    }
    
    int main(){
        int n=read<int>(),p[N];
        for(int i=2;i<=n;++i) read(p[i]);
        for(int i=1;i<=n;++i) read(s[i]),push_up(i);
        for(int i=2;i<=n;++i) link(i,p[i]);
        make_root(1);
        printf("%.9lf
    ",1.0*(A[1][0]-B[1][0])/n/n);
        for(int q=read<int>();q--;){
            char o[2];
            scanf("%s",o);
            if(o[0]=='P'){
                int x=read<int>(),y=read<int>();
                make_root(1),access(y),splay(y);
                int i=x;
                while(nroot(i)) i=fa[i];
                if(i==y) swap(x,y);
                access(x),splay(x);
                fa[ch[x][0]]=0,ch[x][0]=0;
                push_up(x);
                link(x,y);
            }
            else{
                int x=read<int>();
                make_root(x);
                read(s[x]);
                push_up(x);
            }
            make_root(1);
            printf("%.9lf
    ",1.0*(A[1][0]-B[1][0])/n/n);
        }
        return 0;
    }
    
    静渊以有谋,疏通而知事。
  • 相关阅读:
    RK Android7.1 电池电量
    Bat
    RK: 调试 4G模块移远 EC600S-CN
    RK: 调试4G模块 合宙Air720
    关系代数 wiki
    大端与小端的区别
    Microsoft 365 解决方案:如何基于已存在的列表或Excel新建列表
    Microsoft 365 新功能速递:Teams的会议记录将支持对内外部用户共享等新用户体验
    Microsoft 365 解决方案:Office 365 ATP 使用户的收件箱免受钓鱼攻击
    O365事件ID MO222965 -无法访问 Microsoft 365服务
  • 原文地址:https://www.cnblogs.com/autoint/p/15488868.html
Copyright © 2011-2022 走看看