這是一道非常 NB 的題意轉化題.
Introduction
給定一棵樹, 維護以下3個操作:
1 x
表示如果節點 \(x\) 為白色, 則將其染黑. 否則對這個節點的所有兒子遞迴進行相同操作;2 x
表示將以節點 \(x\) 為 \(root\) 的子樹染白;3 x
表示查詢節點 \(x\) 的顏色.
Striking Ideas
修改複雜度遠遠高於詢問複雜度. 考慮平衡操作.
先不考慮操作二.
考慮將對兒子遞迴轉化成對該節點進行資訊修改, 可以發現如果考慮對一個已染色的節點再次染色, 相當於它被染色了 \(2\) 次, 也就等價於它的兒子節點都被染色.
為此, 我們發現, 如果將染黑一個節點表示為將該節點的權值 \(+1\), 則有如果一個節點是黑色, 則一定存在一個祖先節點 (可以是它本身), 使得路徑上的權值之和大於等於距離. 為了方便做題, 我們將初始權值設定為 \(-1\), 則只需要最大字尾和 \(\geq 0\) 即可.
現在考慮操作二.
如果讓子樹染白, 即使子樹內所有的節點的最大字尾和為 \(-1\), 可以先直接把子樹內節點權值賦為 \(-1\), 再給子樹根節點 \(u\) 的權值減少 \(v\), 使得 \(u\) 的最大字尾和為 \(-1\), 容易得到 \(v = query(u) + 1\), 其中 \(query(u)\) 表示最大字尾和.
Algorithm
直接樹鏈剖分, 再用線段樹維護最大字尾和即可. 時間複雜度 \(\mathcal O(n\log ^2n)\).
Code
#include <bits/stdc++.h>
using namespace std;
using ci = const int;
using i64 = long long;
using u64 = unsigned i64;
using u32 = unsigned;
const int N = 1e5 + 5;
const int inf = 0x3f3f3f3f;
int n, m;
vector<int> e[N];
int fa[N], siz[N], dep[N], son[N], top[N], dfn[N], rnk[N], tcnt;
void dfs1(int u) {
siz[u] = 1;
for (ci &v : e[u]) {
if (v == fa[u]) continue;
fa[v] = u;
dep[v] = dep[u] + 1;
dfs1(v);
siz[u] += siz[v];
if (siz[v] > siz[son[u]])
son[u] = v;
}
}
void dfs2(int u) {
rnk[dfn[u] = ++tcnt] = u;
if (son[fa[u]] != u) top[u] = u;
else top[u] = top[fa[u]];
if (son[u]) dfs2(son[u]);
for (ci &v : e[u]) if (!dfn[v]) dfs2(v);
}
struct Node {
int v, s;
bool la;
Node() {}
Node(int _v, int _s, bool _la) : v(_v), s(_s), la(_la) {}
inline Node operator+(const Node &o) const {
return Node(max(v + o.s, o.v), s + o.s, 0);
}
inline void operator+=(const Node &o) {
*this = *this + o;
}
} tr[N << 2];
#define L (p << 1)
#define R (p << 1 | 1)
void pushdown(int p, int l, int r) {
if (!tr[p].la) return;
int mid = l + r >> 1;
tr[L] = Node(-1, -(mid - l + 1), 1);
tr[R] = Node(-1, -(r - mid), 1);
tr[p].la = 0;
}
void build(int p, int l, int r) {
if (l == r) {
tr[p] = Node(-1, -1, 0);
return;
}
int mid = l + r >> 1;
build(L, l, mid);
build(R, mid + 1, r);
tr[p] = tr[L] + tr[R];
}
void cover(int p, int l, int r, ci &al, ci &ar) {
if (al <= l && ar >= r) {
tr[p] = Node(-1, -(r - l + 1), 1);
return;
}
pushdown(p, l, r);
int mid = l + r >> 1;
if (al <= mid) cover(L, l, mid, al, ar);
if (ar > mid) cover(R, mid + 1, r, al, ar);
tr[p] = tr[L] + tr[R];
}
void add(int p, int l, int r, ci &x, ci &y) {
if (l == r) {
tr[p].v += y;
tr[p].s += y;
return;
}
pushdown(p, l, r);
int mid = l + r >> 1;
if (x <= mid) add(L, l, mid, x, y);
else add(R, mid + 1, r, x, y);
tr[p] = tr[L] + tr[R];
}
Node query(int p, int l, int r, ci &al, ci &ar) {
if (al <= l && ar >= r) return tr[p];
pushdown(p, l, r);
int mid = l + r >> 1;
Node num(-inf, 0, 0);
if (al <= mid) num += query(L, l, mid, al, ar);
if (ar > mid) num += query(R, mid + 1, r, al, ar);
return num;
}
#undef L
#undef R
inline int ask(int u) {
Node num(-inf, 0, 0);
while (u) {
num = query(1, 1, n, dfn[top[u]], dfn[u]) + num;
u = fa[top[u]];
}
return num.v;
}
int main() {
#ifndef ONLINE_JUDGE
freopen(".in", "r", stdin);
freopen(".out", "w", stdout);
#endif
scanf("%d %d", &n, &m);
for (int i = 2, x; i <= n; ++i) {
scanf("%d", &x);
e[x].push_back(i);
}
dfs1(1);
dfs2(1);
build(1, 1, n);
while (m--) {
static int op, x;
scanf("%d %d", &op, &x);
switch (op) {
case 1: add(1, 1, n, dfn[x], 1); break;
case 2:
cover(1, 1, n, dfn[x], dfn[x] + siz[x] - 1);
add(1, 1, n, dfn[x], -ask(x) - 1); break;
case 3: puts(ask(x) >= 0 ? "black" : "white"); break;
}
}
return 0;
}