动态树问题和Link Cut Tree

  动态树问题是一类要求维护一个有根树森林,支持对树的分割, 合并等操作的问题。

  Link Cut Tree林可砍树?简称LCT)是解决这一类问题的一种数据结构。

一些无聊的定义

  Link Cut Tree维护的是动态森林中每棵树的任意链剖分。

  Preferred Child,每个点偏好的子节点,对于每个点,要么它没有,要么它只有一个。

  Preferred Edge,连接每个点和它偏好的子节点的边,以下简称为实边

  相对地,对于非实边的边,以下简称为虚边。

  Preferred Path,由Preferred Edge 连接成的不可再延伸的路径。以下简称为实链。特殊地,如果与一个点相连的所有边都是虚边,那么这一个点独自构成一条实链。

  显然,所有实链覆盖所有的点。

  对于每条实链,LCT用一颗Splay对节点按深度为关键字进行维护。

//接下来会默认读者能熟练地敲打Splay的板子

  为了方便直接用Splay森林来维护。对于每棵Splay,它还需要维护它的维护的实链的顶端(深度最小的点)的父节点,这个记在根结点上。

access操作

  access操作是将某个节点$x$到根的路径变为实链。

Link Cut Tree学习笔记

  由图中可以看出,access操作可以看成以下几个操作的反复进行:

  1. 断掉当前实链中当前点和比它深的点之间的实边
  2. 合并当前实链和上一条实链
  3. 访问实链顶端的父节点 

  当当前点为空的时候结束,不存在上一条实链的时候它为空。

  显然这样操作恰好将指定点到根的路径变为实链了。

1 void access(SplayNode *p) {
2     SplayNode* q = NULL;
3     while (p) {
4         splay(p);
5         q = p, p = p->fa;
6     }
7 }

换根操作

  将一棵树的根变为指定点。

  考虑换根操作的影响:

Link Cut Tree学习笔记

  只是旧根到新根的路径被翻转了。那就先对新根执行access操作,然后打反转标记就好了。

1 void mkroot(SplayNode* node) {
2     access(node);
3     splay(node);
4     node->rev ^= 1;
5 }

link和cut操作

  link操作是连接两个不连通的点,cut操作是删除一条原有的边。

  考虑link操作,将其中一个作为根然后向另一个点连接一条虚边即可。

  考虑cut操作,将其中一个作为根,然后对另一个点做access操作,这样恰好根所在的Splay中的后继是另一个点,Splay树上断掉这条边即可。

 1 void link(int u, int v) {
 2     SplayNode* p = pool + u, *q = pool + v;
 3     if(isConnected(p, q))    return ;
 4     mkroot(q);
 5     q->fa = p, splay(q);
 6 }
 7         
 8 void cut(int u, int v) {
 9     SplayNode* p = pool + u, *q = pool + v;
10     mkroot(p);
11     access(q);
12     splay(q);
13     q->ch[0] = p->fa = NULL;
14 }

例1 Cave 洞穴勘测

题目大意

  给定一个动态森林,要求支持加边、删边和询问两点之间的连通性。

  判断两点之间是否连通,等价于它们所在的树的根相同。

  所以考虑如何找一个点所在树的根。

  首先access这个点,然后把它伸展到根,然后暴力跳左子树就好了。

Code

  1 /**
  2  * bzoj
  3  * Problem#2049
  4  * Accepted
  5  * Time: 1976ms
  6  * Memory: 1500k
  7  */
  8 #include <bits/stdc++.h>
  9 using namespace std;
 10 typedef bool boolean;
 11 
 12 typedef class SplayNode {
 13     public:
 14         boolean rev;
 15         SplayNode *ch[2];
 16         SplayNode *fa;
 17         
 18         SplayNode():rev(0), ch({NULL, NULL}), fa(NULL) {        }
 19         
 20         boolean isRoot() {
 21             if(fa == NULL)    return true;
 22             return fa->ch[0] != this && fa->ch[1] != this;
 23         }
 24         
 25         void pushDown() {
 26             swap(ch[0], ch[1]);
 27             if(ch[0])    ch[0]->rev ^= 1;
 28             if(ch[1])    ch[1]->rev ^= 1;
 29             rev = 0;
 30         }
 31 }SplayNode;
 32 
 33 typedef class LinkCutTree {
 34     public:
 35         SplayNode *pool;
 36         
 37         LinkCutTree():pool(NULL) {        }
 38         LinkCutTree(int n) {
 39             pool = new SplayNode[(n + 1)];
 40         }
 41         
 42         void rotate(SplayNode* node) {
 43             SplayNode* fa = node->fa;
 44             boolean aFlag = fa->isRoot();
 45             int d = fa->ch[1] == node;
 46             fa->ch[d] = node->ch[d ^ 1];
 47             node->fa = fa->fa;
 48             fa->fa = node;
 49             node->ch[d ^ 1] = fa;
 50             if(fa->ch[d])    fa->ch[d]->fa = fa;
 51             if(!aFlag) node->fa->ch[node->fa->ch[1] == fa] = node;
 52         }
 53         
 54         SplayNode* s[10005];
 55         void splay(SplayNode* node) {
 56             int top = 0;
 57             s[++ top] = node;
 58             for (SplayNode* p = node; !p -> isRoot(); p = p->fa) s[++ top] = p -> fa; 
 59             for (int i = top; i; -- i) if (s[i] -> rev) s[i] -> pushDown();
 60             while(!node->isRoot()) {
 61                 SplayNode *fa = node->fa, *ffa = fa->fa;
 62                 if(fa->isRoot()) {
 63                     rotate(node);
 64                     break;
 65                 }
 66                 int d1 = (fa->ch[1] == node), d2 = ffa->ch[1] == fa;
 67                 if(d1 == d2)
 68                     rotate(fa);
 69                 else
 70                     rotate(node);
 71                 rotate(node);
 72             }
 73         }
 74         
 75         SplayNode* findSplayRoot(SplayNode* node) {
 76             access(node);
 77 //            while(node->fa)    node = node->fa;
 78             splay(node);
 79             while(node->ch[0])    node = node->ch[0];
 80             return node;
 81         }
 82         
 83         void access(SplayNode* node) {
 84             SplayNode* p = NULL;
 85             do {
 86                 splay(node);
 87                 node->ch[1] = p;
 88                 p = node;
 89                 node = node->fa;
 90             } while (node);
 91         }
 92         
 93         boolean isConnected(SplayNode* p, SplayNode* q) {
 94             return findSplayRoot(p) == findSplayRoot(q);
 95         }
 96         
 97         void mkroot(SplayNode* node) {
 98             access(node);
 99             splay(node);
100             node->rev ^= 1;
101         }
102         
103         void link(int u, int v) {
104             SplayNode* p = pool + u, *q = pool + v;
105             if(isConnected(p, q))    return ;
106             mkroot(q);
107             q->fa = p, splay(q);
108         }
109         
110         void cut(int u, int v) {
111             SplayNode* p = pool + u, *q = pool + v;
112             mkroot(p);
113             access(q);
114             splay(q);
115             q->ch[0] = p->fa = NULL;
116         }
117         
118         boolean isConnected(int u, int v) {
119             return isConnected(pool + u, pool + v);
120         }
121 }LinkCutTree;
122 
123 int n, m;
124 LinkCutTree lct;
125 char buf[15];
126 int u, v;
127 inline void solve() {
128     scanf("%d%d", &n, &m);
129     lct = LinkCutTree(n);
130     while(m--) {
131         scanf("%s%d%d", buf, &u, &v);
132         switch(buf[0]) {
133             case 'C':
134                 lct.link(u, v);
135                 break;
136             case 'D':
137                 lct.cut(u, v);
138                 break;
139             case 'Q':
140                 puts((lct.isConnected(u, v)) ? ("Yes") : ("No"));
141                 break;
142         }
143     }
144 }
145 
146 int main() {
147     solve();
148     return 0;
149 }
View Code

相关文章:

  • 2021-06-10
  • 2021-11-04
  • 2022-01-14
  • 2021-10-20
  • 2022-12-23
  • 2022-12-23
  • 2022-02-15
  • 2022-02-02
猜你喜欢
  • 2022-01-15
  • 2021-12-27
  • 2022-12-23
  • 2021-12-02
  • 2021-09-24
  • 2021-07-10
  • 2021-11-17
相关资源
相似解决方案