SPOJ 16549 - QTREE6 - Query on a tree VI 「一种维护树上颜色连通块的操作」
Posted colythme
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SPOJ 16549 - QTREE6 - Query on a tree VI 「一种维护树上颜色连通块的操作」相关的知识,希望对你有一定的参考价值。
题意
有操作
$0$ $u$:询问有多少个节点 $v$ 满足路径 $u$ 到 $v$ 上所有节点(包括)都拥有相同的颜色
$1$ $u$:翻转 $u$ 的颜色
题解
直接用一个 $LCT$ 去暴力删边连边显然会 $T$
那么只有两个颜色的话就可以建两棵 $LCT$ ,观察到每次单点修改颜色时其子树所包含连通块在原颜色树上与其父亲所代表连通块断开,所以可以看作断开与父节点的边(实际上是点化边的思想),那么其它常规操作即可
注意要建个虚拟节点作为根节点的父亲
注意 $0$ 操作询问的输出,详细解释有在代码注释中给出
代码
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 5 using namespace std; 6 7 const int MAXN = 1e06 + 10; 8 const int MAXM = 1e06 + 10; 9 10 struct LinkedForwardStar { 11 int to; 12 13 int next; 14 } ; 15 16 LinkedForwardStar Link[MAXM << 1]; 17 int Head[MAXN]= {0}; 18 int size = 0; 19 20 void Insert (int u, int v) { 21 Link[++ size].to = v; 22 Link[size].next = Head[u]; 23 24 Head[u] = size; 25 } 26 27 int N, M; 28 int ances[MAXN]; 29 30 struct Link_Cut_Tree { 31 int father[MAXN]; 32 int son[MAXN][2]; 33 int subtree[MAXN], virsize[MAXN]; 34 35 void init () { 36 for (int i = 1; i <= N; i ++) 37 father[i] = son[i][0] = son[i][1] = subtree[i] = virsize[i] = 0; 38 } 39 int isroot (int p) { 40 return son[father[p]][0] != p && son[father[p]][1] != p; 41 } 42 int sonbel (int p) { 43 return son[father[p]][1] == p; 44 } 45 void pushup (int p) { 46 subtree[p] = subtree[son[p][0]] + subtree[son[p][1]] + virsize[p] + 1; 47 } 48 void rotate (int p) { 49 int fa = father[p], anc = father[fa]; 50 int s = sonbel (p); 51 son[fa][s] = son[p][s ^ 1]; 52 if (son[fa][s]) 53 father[son[fa][s]] = fa; 54 if (! isroot (fa)) 55 son[anc][sonbel (fa)] = p; 56 father[p] = anc; 57 son[p][s ^ 1] = fa, father[fa] = p; 58 pushup (fa), pushup (p); 59 } 60 void splay (int p) { 61 for (int fa = father[p]; ! isroot (p); rotate (p), fa = father[p]) 62 if (! isroot (fa)) 63 sonbel (p) == sonbel (fa) ? rotate (fa) : rotate (p); 64 } 65 void Access (int p) { 66 for (int tp = 0; p; tp = p, p = father[p]) { 67 splay (p); 68 virsize[p] += subtree[son[p][1]]; 69 son[p][1] = tp; 70 virsize[p] -= subtree[son[p][1]]; 71 pushup (p); 72 } 73 } 74 int findroot (int p) { 75 Access (p), splay (p); 76 while (son[p][0]) 77 p = son[p][0]; 78 splay (p); 79 return p; 80 } 81 void link (int p) { 82 int fa = ances[p]; 83 splay (p); 84 father[p] = fa; 85 Access (fa), splay (fa); 86 subtree[fa] += subtree[p], virsize[fa] += subtree[p]; 87 } 88 void cut (int p) { 89 Access (p), splay (p); 90 father[son[p][0]] = 0, son[p][0] = 0; 91 pushup (p); 92 } 93 } ; 94 Link_Cut_Tree LCT[2]; 95 96 void DFS (int root, int father) { 97 ances[root] = father; 98 LCT[0].link (root); 99 for (int i = Head[root]; i; i = Link[i].next) { 100 int v = Link[i].to; 101 if (v == father) 102 continue; 103 DFS (v, root); 104 } 105 } 106 107 int Colour[MAXN]= {0}; 108 109 int getnum () { 110 int num = 0; 111 char ch = getchar (); 112 113 while (! isdigit (ch)) 114 ch = getchar (); 115 while (isdigit (ch)) 116 num = (num << 3) + (num << 1) + ch - ‘0‘, ch = getchar (); 117 118 return num; 119 } 120 121 int main () { 122 N = getnum (); 123 for (int i = 1; i <= N; i ++) 124 LCT[0].subtree[i] = LCT[1].subtree[i] = 1; 125 for (int i = 1; i < N; i ++) { 126 int u = getnum (), v = getnum (); 127 Insert (u, v), Insert (v, u); 128 } 129 DFS (1, N + 1); 130 M = getnum (); 131 for (int Case = 1; Case <= M; Case ++) { 132 int opt = getnum (), p = getnum (); 133 int col = Colour[p]; 134 if (opt == 0) { 135 int anc = LCT[col].findroot (p); 136 printf ("%d ", LCT[col].subtree[LCT[col].son[anc][1]]); 137 // 注意,因为有可能存在两个不连通的连通快在LCT上连通,又在Access后右节点仅包含当前链 138 // 故需输出右子树信息而并非减一,否则有可能会算上另一个连通块的答案 139 } 140 else if (opt == 1) 141 LCT[col].cut (p), LCT[Colour[p] ^= 1].link (p); 142 } 143 144 return 0; 145 } 146 147 /* 148 5 149 1 2 150 1 3 151 1 4 152 1 5 153 3 154 0 1 155 1 1 156 0 1 157 */ 158 159 /* 160 5 161 1 2 162 2 3 163 3 4 164 4 5 165 3 166 1 1 167 1 3 168 0 1 169 */
以上是关于SPOJ 16549 - QTREE6 - Query on a tree VI 「一种维护树上颜色连通块的操作」的主要内容,如果未能解决你的问题,请参考以下文章
SPOJ QTREE6 - Query on a tree VI(lct)