【动态树】【Link Cut Tree】动态树的理解(入门)

引入

现在我们需要一个数据结构满足支持以下的操作:

  1. 两个节点连接(保证不出现环)
  2. 两个节点断开
  3. 求任意两个节点之间的区间和

这样是不是很像树链剖分? 但是因为是动态的所以我们采用动态树来进行维护。

样例

现在给出一个样例,我们一下的解释都以当前样例为模板
我们有三个节点1 2 3 4,现在他们是连接在一起的如下图
【动态树】【Link Cut Tree】动态树的理解(入门)_第1张图片
为什么有一个虚线呢,这里我们用虚线表示假装连接在了一起(这里下文会讲到)

操作

首先我们因为需要维护的是一棵树,我们利用树链剖分的思路,我们将当前树分为许多条链分别用Splay进行维护,这样我们就用上图的表示1->2是类似于轻边的东西,1->3是类似于重边的东西,但是这都是临时的
那么我们因为需要一条链那么我们需要以下的操作

操作一(一切的基础)

我们利用Access(u)表示从当前根到u变为重边,同时让这条链独立出来,那么我们可以通过Access(2)得到以下的图
【动态树】【Link Cut Tree】动态树的理解(入门)_第2张图片
这样1->2就成为了一条链,同时将3孤立出去了

操作二(Splay=神奇的相对位置)

我们通过一个Splay来维护每一条链(的深度),在每一条链成为一个Splay进行旋转的时候,位置并不是不改变的对于上图我们有三个Splay
1、1->2
2、4
3、3
这样的三棵树我们在旋转Splay的同时每一个Splay的根的父亲表示的是当前链的最顶端的父亲(也就是深度最浅的那个节点),并不是Splay的root节点的父亲,这样我们保存的就是相对位置就像我们使用Splay(2)上面的图就会转成
【动态树】【Link Cut Tree】动态树的理解(入门)_第3张图片
通过观察可以发现其实相对的位置其实是没有改变的。

操作三(make_root=让整个树换个方向吧)

其实我们的动态树没有动他会感到寂寞
我们可以发现对于任意一个节点都可以成为新的根,同时其他的都可以跟着翻转就行了,那么我们首先想要成为Boss我们第一步就是进入和根连接的中心链中,这样make_root(u)首先我们要Access(u)比如我们让4成为根,我们需要Access(4)然后就变成了【动态树】【Link Cut Tree】动态树的理解(入门)_第4张图片
看上去这个图中4在2的左边儿子其实因为这个Splay我并没有画出来,他其实是长成这个样子的
【动态树】【Link Cut Tree】动态树的理解(入门)_第5张图片
这样此时我们Splay(4)就成了
【动态树】【Link Cut Tree】动态树的理解(入门)_第6张图片
当然我们发现这个时候我们的2不是还是在4号节点的左边吗?但是对于一个平衡树的概念我们是应该翻转一下的这样我们的4就没有儿子比他深度更浅啦啦啦啦~然后我们给4号节点打上标记rev同时我们将左右儿子交换【动态树】【Link Cut Tree】动态树的理解(入门)_第7张图片
注意我们在Splay(u)的同时要记得从u所在的Splay的根部从上一直到u下放标记同时交换左右子树

操作四(Link=开始连接吧)

我们如果需要进行连接想一想并查集。。。。我们首先找到x和y的根节点然后让root(x)成为root(y)的儿子。。。。。这样我们对于动态树采取同样的方法,首先我们make_root(x)这样x就成为了一个完整的树的顶端 这样我们就可以不用考虑x的儿子的问题啦此时Fa(x)一定是等于0的那么我们可以让Fa(x)=y好啦就是这样。。。
同样我们举个例子假设我们有1, 2成为一棵树, 3, 4成为了一棵树,那么我们如下:那么Link(2, 4)
【动态树】【Link Cut Tree】动态树的理解(入门)_第8张图片
我们得到首先:make_root(2)
【动态树】【Link Cut Tree】动态树的理解(入门)_第9张图片
然后设置Fa(2)=4
【动态树】【Link Cut Tree】动态树的理解(入门)_第10张图片
这里我们的rev2是作为4的左边儿子出现的因为为了维护之前的相对位置是链的顶端的性质,同理我们有

操作五(断开吧)

既然可以连接我们一定可以断开天下没有不散的宴席这样我们可以得到断开的方法,首先Cut(x,y)表示断开x和y的连接,那么我们同理首先make_root(x)这样x就成为了整个树的顶端此时用Access(y)那么y和x一定是在同一条链上然后我们使用Splay将y旋转到顶部,因为x和y一定相连,同时Splay并不会干扰到x的深度,那么在Splay之后x的深度一定是y的深度+1同时因为x和y相连所以我们判断x和y一定是x是y的左儿子(为什么不是右儿子那是因为我们之前已经make_root(x)所以可以保证没有深度比x更浅的啦啦啦啦啦~~~~

代码

代码时间到:

#include 
#include 
#include 
#include 
using namespace std;
const int MAXN = 10000;
int ch[MAXN+10][2], Fa[MAXN+10], rev[MAXN+10], que[MAXN+10];
bool isroot(int u){
    if(!Fa[u]) return 1;
    return ch[Fa[u]][0] != u && ch[Fa[u]][1] != u;
}
void Rotate(int u){
    bool d = ch[Fa[u]][1] == u;
    int x = Fa[u], y = Fa[x];
    Fa[u] = y;
    if(!isroot(x)) ch[y][ch[y][1] == x] = u;
    ch[x][d] = ch[u][!d]; Fa[ch[u][!d]] = x;
    ch[u][!d] = x; Fa[x] = u;
}
void push_down(int u){
    if(rev[u]){
        rev[ch[u][0]]^=1;
        swap(ch[ch[u][0]][0], ch[ch[u][0]][1]);
        rev[ch[u][1]]^=1;
        swap(ch[ch[u][1]][0], ch[ch[u][1]][1]);
        rev[u] ^= 1;
    }
}
void Splay(int u){
    int top = 0;
    que[++top] = u;
    for(int i=u;!isroot(i);i=Fa[i])
        que[++top] = Fa[i];
    top++;
    while(--top) push_down(que[top]);
    while(!isroot(u)){
        int x = Fa[u], y = Fa[x];
        if(!isroot(x)){
            if((ch[y][0] == x) ^ (ch[x][0] == u)) Rotate(x);
            else Rotate(u);
        }
        Rotate(u);
    }
}
void Access(int u){
    int t = 0;
    while(u){
        Splay(u);
        ch[u][1] = t;
        t = u;
        u = Fa[u];
    }
}
void reset_root(int u){
    Access(u);
    Splay(u);
    rev[u] ^= 1;
    swap(ch[u][0], ch[u][1]);
}
void Link(int x, int y){
    reset_root(x);
    Fa[x] = y;
    Splay(x);
}
void Cut(int x, int y){
    reset_root(x);
    Access(y);
    Splay(y);
    ch[y][0] = Fa[x] = 0;
}

感谢

感谢您阅读这篇文章,如果有不足的地方请评论

你可能感兴趣的:(数据结构,动态树,Splay,BZOJ,图论——动态树,图论)