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\n", 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 */

 

转载于:https://www.cnblogs.com/Colythme/p/10218051.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值