1. 程式人生 > 其它 >題解 P6018 [Ynoi2010] Fusion tree

題解 P6018 [Ynoi2010] Fusion tree

發現每個點距離為 \(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\)

,要把它刪除等同於再加一個 \(x\),這樣異或和為 \(0\) 了。所以其實沒有必要寫兩個函式(但是好像基本上題解都寫了)。

如何處理全域性 \(+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);
}

於是就可以維護了。注意一些實現的細節。

提交記錄程式碼實現