zoukankan      html  css  js  c++  java
  • [Link-Cut-Tree]【学习笔记】

    可以按照<Utopiosphere>的调唱出来 “Link-Cut ,Time doesn’t stop .Prepare your doubts ,Eat them up”

    参考资料:

    1.popoqqq课件

    2.《QTREE 解法的一些研究 》

    3.http://blog.csdn.net/clove_unique/article/details/50991804

    【理论知识】

    • Link-Cut-Tree(简称LCT)是解决动态树类问题一种数据结构
    • Preferred Child:重儿子,重儿子与父亲节点在同一棵Splay中,一个节点最多只能有一个重儿子
    • Preferred Edge:重边,连接父亲节点和重儿子的边
    • Preferred Path :重链,由重边及重边连接的节点构成的链

    Auxiliary Tree(辅助树)

    • 由一条重链上的所有节点所构成的Splay称作这条链的辅助树
    • 每个点的键值为这个点的深度,即这棵Splay的中序遍历是这条链从链顶到链底的所有节点构成的序列
    • 辅助树的根节点的父亲指向链顶的父亲节点,然而链顶的父亲节点的儿子并不指向辅助树的根节点
    • (也就是说父亲不认轻儿子只认重儿子,儿子都认父亲)
    • 这条性质为后来的操作提供了依据

    原树与辅助树的关系

    • 原树中的重链 -> 辅助树中两个节点位于同一棵Splay
    • 原树中的轻链 -> 辅助树中子节点所在Splay的根节点的father指向父节点
    • 注意原树与辅助树的结构并不相同
    • 辅助树的根节点≠原树的根节点
    • 辅助树中的father≠原树中的father

    辅助树是不断变化的,重链和轻链不断变化

    二【实现】

    LCT用到的Splay和通常的还是有点不同,没有权值v,不进行查找操作,点编号就是原树的编号

    因为是一个Splay森林,多条重链多个根,所以用isRoot(x)判断是否为根,判断isRoot(x)相当于判断x的父亲存不存在

    rotate只是设置g的儿子时判断isRoot(f)就行了

    splay需要pushDown了(因为没有kth了),也是判断isRoot(pa)

    Access和Cut更新了儿子关系,所以需要update

    Access 

    • 将一个点与原先的重儿子切断,并使这个原树上点到根路径上的边全都变为重边
    • 所以 这个节点到根的路径上的所有节点形成了一棵Splay
    • 便于操作或查询节点到根路径上的所有节点

    实现:不断把x splay到当前Atree的根,然后它的右子树就是重儿子了,修改;用y辅助

    注意:Access后x不一定为这颗Splay的根,因为中途x变fa了

    维护了节点信息别忘更新

    MakeRoot

    • 将x设为原树的根

    实现:Access后splay到根,然后全在x的左子树上(权值是深度),区间翻转即可

    FindRoot

    • 找x所在原树根,判连通性

    实现:MakeRoot后不断往左找(不需要pushDown?加上也可以啊。不加也对因为只是来判连通,判断是不是在一棵原树上,都不pushDown找到的还是同一个点吧)

    Link

    实现:MakeRoot(x)然后t[x].fa=y

    Cut

    实现:MakeRoot(x)然后Access(y) splay(y) ,x就在y的左儿子了,t[y].ch[0]=t[x].fa=0;

    维护了节点信息别忘更新

    对x到y路径上的点进行修改或查询
    只需要对x进行Move_To_Root操作,然后对y进行Access+Splay操作,那么x到y路径上的所有点都在以y为根的子树上

    因为Access后x和y重链在一棵Splay上,x深度比y小

    三【模板】

    [update 2017-04-05]

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    using namespace std;
    #define lc t[x].ch[0]
    #define rc t[x].ch[1]
    #define pa t[x].fa
    typedef long long ll;
    const int N=3e5+5;
    inline int read(){
        char c=getchar();int x=0,f=1;
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    
    namespace lct {
        struct meow{int ch[2], fa, rev, sum, w;} t[N];
        inline int wh(int x) {return t[pa].ch[1] == x;}
        inline int isr(int x) {return t[pa].ch[0] != x && t[pa].ch[1] != x;}
        inline void update(int x) {t[x].sum = t[lc].sum ^ t[rc].sum ^ t[x].w;}
        inline void rever(int x) {t[x].rev ^= 1; swap(lc, rc);}
        inline void pushdn(int x) {
            if(t[x].rev) {
                if(lc) rever(lc);
                if(rc) rever(rc);
                t[x].rev = 0;
            }
        }
        void pd(int x) {if(!isr(x)) pd(pa); pushdn(x);}
        inline void rotate(int x) {
            int f=t[x].fa, g=t[f].fa, c=wh(x);
            if(!isr(f)) t[g].ch[wh(f)]=x; t[x].fa=g;
            t[f].ch[c] = t[x].ch[c^1]; t[t[f].ch[c]].fa=f;
            t[x].ch[c^1] = f; t[f].fa=x;
            update(f); update(x);
        }
        inline void splay(int x) {
            pd(x);
            for(; !isr(x); rotate(x))
                if(!isr(pa)) rotate( wh(pa)==wh(x) ? pa : x );
        }
    
        inline void access(int x) {
            for(int y=0; x; y=x, x=pa) splay(x), rc=y, update(x);
        }
        inline void maker(int x) {
            access(x); splay(x); rever(x);
        }
        inline int findr(int x) {
            access(x); splay(x);
            while(lc) pushdn(x), x=lc; return x;
        }
        inline void link(int x, int y) {
            maker(x); t[x].fa=y;
        }
        inline void cut(int x, int y) {
            maker(x); access(y); splay(y);
            t[x].fa = t[y].ch[0] = 0; update(y);
        }
        inline void split(int x, int y) {
            maker(x); access(y); splay(y);
        }
    } using lct::findr;
    
    int n, Q, op, x, y;
    int main() {
        freopen("in","r",stdin);
        n=read(); Q=read();
        for(int i=1; i<=n; i++) lct::t[i].w = read();
        for(int i=1; i<=Q; i++) {
            op=read(); x=read(); y=read();
            if(op==0) lct::split(x, y), printf("%d
    ", lct::t[y].sum);
            if(op==1) if(findr(x) != findr(y)) lct::link(x, y);
            if(op==2) if(findr(x) == findr(y)) lct::cut(x, y);
            if(op==3) lct::t[x].w = y, lct::splay(x);
        }
    }
    View Code

    四【一点好玩的东西】

    1.LCT可做动态树问题

    2.LCT可做树链剖分

    3.LCT可做支持删除边的并查集(我太navie了.......并不能完全实现这个功能,是一颗树啊啊啊)

    4.LCT可做不用排序的Kruskal(动态加边的最小生成树)

  • 相关阅读:
    计算机图形学——几何变换的数学基础
    算法设计与分析——多边形游戏(动态规划)
    算法设计与分析——凸多边形最优三角剖分(动态规划)
    计算机图形学——反走样
    Android 5.0 API
    Android 6.0 API
    Android 7.0 新增功能和api
    Android 8.0 功能和 API
    Android P 功能和 API
    解决华为手机无法输出Debug级别log的问题
  • 原文地址:https://www.cnblogs.com/candy99/p/6271344.html
Copyright © 2011-2022 走看看