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)

bzoj3637 CodeChef SPOJ - QTREE6 Query on a tree VI 题解

SPOJ QTREE7

SPOJQTREE7(Link-Cut Tree)

Query on a tree VI [SP16549]

SPOJQTREE6(Link-Cut-Tree)