• 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;
    }
    
    静渊以有谋,疏通而知事。
  • 相关阅读:
    Ogre参考手册(九) 5 硬件缓冲区
    Ogre参考手册(十二) 8 动画
    Ogre参考手册(三)3.1.3 纹理单元TextureUnit
    在Ogre中直接使用D3D
    Ogre参考手册(四)3.1.43.1.14 声明顶点、几何、片段程序(Ogre着色器脚本)
    制作立体图像(上):红蓝眼镜原理
    算法学习(java实现)
    JAVA 的data类型 long类型 生成星期几汇总
    Android R.java解析
    Myeclipse的使用方法查找类文件(Open Type)
  • 原文地址:https://www.cnblogs.com/autoint/p/15488868.html
Copyright © 2020-2023  润新知