題解 P6018 [Ynoi2010] Fusion tree
阿新 • • 發佈:2021-10-28
發現每個點距離為 \(1\) 的節點就是兒子或者父親,因此可以把兒子和父親分開來算。
計算父親是很容易的,直接維護 \(a\) 的值。對於操作 \(1\) ,在父親上標記就行了,表示這個點進行過幾次的操作 \(1\)。
對於每個節點維護兒子,就會發現是要維護:單點加入、單點刪除、全域性 \(+1\),全域性異或和。
如果對 01-trie 比較熟悉,可以發現 01-trie 可以維護這個東西。從低位到高位建立 01-trie,大致是要維護每個節點的子樹的異或和 \(val\),還有每個節點到父親的邊權 \(w\)(指這條邊被經過了幾次)。
由於是異或,會發現可以只考慮 \(w\) 的奇偶性。於是 push_up 大致是這樣的:
inline void push_up(int u)
{
w[u]=0,val[u]=0;
if (nxt[u][0])
{
w[u]^=w[nxt[u][0]];
val[u]^=(val[nxt[u][0]]<<1);
}
if (nxt[u][1])
{
w[u]^=w[nxt[u][1]];
val[u]^=(val[nxt[u][1]]<<1);
if (w[nxt[u][1]]==1) val[u]^=1;
}
}
注意到只考慮 \(w\) 的奇偶性,因此 insert 和 delete 本質相同。你可以理解為,先加入一個數 \(x\)
如何處理全域性 \(+1\)?從低到高考慮,每一位 \(1\) 變成 \(0\) 同時進位,\(0\) 變成 \(1\) 不進位。遞迴做就行了。
void add(int x)
{
swap(nxt[x][0],nxt[x][1]);
if (nxt[x][0]) add(nxt[x][0]);
push_up(x);
}
於是就可以維護了。注意一些實現的細節。