1. 程式人生 > >SPOJ 16549 - QTREE6 - Query on a tree VI 「一種維護樹上顏色連通塊的操作」

SPOJ 16549 - QTREE6 - Query on a tree VI 「一種維護樹上顏色連通塊的操作」

amp .so 兩個 += sin get div str shu

題意

有操作

$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 */

SPOJ 16549 - QTREE6 - Query on a tree VI 「一種維護樹上顏色連通塊的操作」