从这里开始
动态树问题和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$到根的路径变为实链。
由图中可以看出,access操作可以看成以下几个操作的反复进行:
- 断掉当前实链中当前点和比它深的点之间的实边
- 合并当前实链和上一条实链
- 访问实链顶端的父节点
当当前点为空的时候结束,不存在上一条实链的时候它为空。
显然这样操作恰好将指定点到根的路径变为实链了。
1 void access(SplayNode *p) { 2 SplayNode* q = NULL; 3 while (p) { 4 splay(p); 5 q = p, p = p->fa; 6 } 7 }
换根操作
将一棵树的根变为指定点。
考虑换根操作的影响:
只是旧根到新根的路径被翻转了。那就先对新根执行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 }