洛谷 P4332 [SHOI2014]三叉神經樹 題解
阿新 • • 發佈:2021-06-18
一、題目:
二、思路:
這道題怎麼說呢?只能說有點意思,讓我第一次見識了LCT怎麼應用。
首先一個非常明顯的性質,就是比如我現在修改了某個葉子結點,記為 \(leaf\),那麼因此而狀態發生改變的點一定是從 \(leaf\) 向上的連續區間。所以我們自然而然能想到兩種資料結構,一種是樹鏈剖分,另一種就是LCT,因為這兩種都可以較好地維護樹鏈的資訊。
這題用LCT怎麼做呢?難點就在於找出每次的連續區間的終點在哪?當然,由於連續區間的性質,我們立刻就能想到二分答案。時間複雜度 \(O(n\log^2 n)\)。
其實,我們還可以通過維護一些資訊來降低一下時間複雜度。具體來說,我們每次打通 \(leaf\)
具體怎麼維護呢?請配合註釋看程式碼。
三、程式碼:
#include <iostream> #include <cstdio> #include <cstring> using namespace std; #define FILEIN(s) freopen(s".in", "r", stdin); #define FILEOUT(s) freopen(s".out", "w", stdout) #define mem(s, v) memset(s, v, sizeof s) inline int read(void) { int x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); } return f * x; } const int maxn = 500005 * 3; int n, m, son[maxn][2], id[maxn][3], fa[maxn], add[maxn], val[maxn], sum[maxn], tot; int head[maxn]; struct Edge { int y, next; Edge() {} Edge(int _y, int _next) : y(_y), next(_next) {} }e[maxn << 1]; inline void connect(int x, int y) { e[++ tot] = Edge(y, head[x]); head[x] = tot; } inline void update(int o) { // 由於Splay結點的中序遍歷代表原樹的一條從上到下的路徑,因此我們必須先看右兒子,再看它自己,最後看它的左兒子。 id[o][1] = id[son[o][1]][1]; id[o][2] = id[son[o][1]][2]; if (!id[o][1]) { if (sum[o] != 1) id[o][1] = o; else id[o][1] = id[son[o][0]][1]; } if (!id[o][2]) { if (sum[o] != 2) id[o][2] = o; else id[o][2] = id[son[o][0]][2]; } } inline void change(int o, int x) { sum[o] += x; val[o] = sum[o] > 1; swap(id[o][1], id[o][2]); // 修改操作一定只對sum值全部為1或2的區間進行,因此我們只需交換id即可。 add[o] += x; } inline void pushdown(int o) { if (add[o]) { if (son[o][0]) change(son[o][0], add[o]); if (son[o][1]) change(son[o][1], add[o]); add[o] = 0; } } inline bool isroot(int x) { return son[fa[x]][0] != x && son[fa[x]][1] != x; } inline int get(int x) { return son[fa[x]][1] == x; } inline void rotate(int x) { int y = fa[x], z = fa[y], k = get(x); pushdown(y); pushdown(x); if (!isroot(y)) son[z][son[z][1] == y] = x; son[y][k] = son[x][k ^ 1]; fa[son[y][k]] = y; fa[y] = x; son[x][k ^ 1] = y; fa[x] = z; update(y); update(x); } void correct(int x) { if (!isroot(x)) correct(fa[x]); pushdown(x); } inline void splay(int x) { correct(x); for (int f = fa[x]; !isroot(x); rotate(x), f = fa[x]) if (!isroot(f)) rotate(get(x) == get(f) ? f : x); } inline void access(int x) { int z = x; for (int y = 0; x; y = x, x = fa[x]) { splay(x); son[x][1] = y; update(x); } splay(z); } void dfs(int x, int fa) { sum[x] = 0; for (int i = head[x], y; i; i = e[i].next) { y = e[i].y; if (y == fa) continue; dfs(y, x); sum[x] += val[y]; } if (x <= n) val[x] = sum[x] > 1; } int main() { n = read(); int x; for (int i = 1; i <= n; ++ i) { for (int j = 1; j <= 3; ++ j) { x = read(); fa[x] = i; connect(x, fa[x]); connect(fa[x], x); } } for (int i = n + 1; i <= n * 3 + 1; ++ i) val[i] = read(); dfs(1, 0); int ans = val[1]; m = read(); while (m --) { int leaf = read(), x = fa[leaf]; int addtag = val[leaf] ? -1 : 1; access(x); int w = id[x][val[leaf] ? 2 : 1]; if (w) { splay(w); change(son[w][1], addtag); sum[w] += addtag; val[w] = sum[w] > 1; update(w); // 由於w的兒子的id發生了變化,注意要update一下,當然換成splay(w)也是可以的。 } else ans ^= 1, change(x, addtag); // 特殊判斷w不存在的情況。 val[leaf] ^= 1; printf("%d\n", ans); } return 0; }