题意
给一棵一开始 \(n\) 个点全是白色的树,以 \(1\) 为根,支持三种操作:
1.将某一个点变黑,如果已经是黑色则该操作对所有儿子生效。
2.将一棵子树改成白色。
3.询问某个点的颜色。
\(n\leq 10^5\)
分析
唯一棘手的问题是操作1,我们很难正面解决这个问题。
考虑点 \(u\) 到根的路径,只有在这些点上的操作才可能影响 \(u\) 的颜色。
\(u\) 变黑的那次操作一定是先选定一个 \(u\) 的祖先,然后祖先到 \(u\) 的路径上所有的点都已经是黑色。先将所有点都标记成 \(-1\) ,表示要将其变黑需要一步,1操作就在对应节点+1。
所以如果一个点是黑色当且仅当他到根路径上存在一个后缀和 \(\geq 0\) 。操作2在将子树变白之后还要考虑子树祖先的影响,可以理解成之前下放的标记废掉了,子树的根节点减去 在根节点查询到的后缀最大值+1 即可。之后如果子树内的点查询时的答案在子树祖先上,那一部分贡献就被删除了。
有没有可能子树的祖先信息修改了,而当前点开始抵消的部分没有撤销?因为覆盖都是子树修改,如果祖先改了撤销信息也没有了,所以不用考虑。
总时间复杂度为 \(O(nlog^2n)\)。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define go(u) for(int i = head[u], v = e[i].to; i; i=e[i].lst, v=e[i].to)
#define rep(i, a, b) for(int i = a; i <= b; ++i)
#define pb push_back
inline int gi() {
int x = 0,f = 1;
char ch = getchar();
while(!isdigit(ch)) {
if(ch == '-') f = -1;
ch = getchar();
}
while(isdigit(ch)) {
x = (x << 3) + (x << 1) + ch - 48;
ch = getchar();
}
return x * f;
}
template <typename T> inline void Max(T &a, T b){if(a < b) a = b;}
template <typename T> inline void Min(T &a, T b){if(a > b) a = b;}
const int N = 1e5 + 7, inf = 0x3f3f3f3f;
int n, q, edc, tim;
int head[N], fa[N], zson[N], son[N], top[N], in[N], out[N];
struct edge {
int lst, to;
edge(){}edge(int lst, int to):lst(lst), to(to){}
}e[N*2];
void Add(int a, int b) {
e[++edc] = edge(head[a], b), head[a] = edc;
e[++edc] = edge(head[b], a), head[b] = edc;
}
void dfs1(int u) {
son[u] = 1;
go(u)if(v ^ fa[u]) {
fa[v] = u, dfs1(v);
son[u] += son[v];
if(son[v] > son[zson[u]]) zson[u] = v;
}
}
void dfs2(int u,int from) {
in[u] = ++tim; top[u] = from;
if(zson[u]) dfs2(zson[u], from);
go(u)if(v ^ fa[u] && v ^ zson[u])
dfs2(v, v);
out[u] = tim;
}
int setv[N << 2];
#define Ls o << 1
#define Rs o << 1 | 1
struct data {
int mx,s;
data(){mx = -inf;}data(int mx, int s):mx(mx), s(s) {}
data operator +(const data &b) const {
return data(max(b.mx, mx + b.s), s + b.s);
}
}t[N << 2];
void md(int l, int r, int o) {
setv[o] = 1;
t[o].mx = -1, t[o].s = -(r - l + 1);
}
void pushdown(int l, int r, int o) {
if(!setv[o]) return;
int mid = l + r >> 1;
md(l, mid, Ls);
md(mid + 1, r, Rs);
setv[o] = 0;
}
void pushup(int o) {
t[o] = t[Ls] + t[Rs];
}
void build(int l, int r, int o) {
if(l == r) {
t[o].mx = t[o].s = -1;
return;
}int mid = l + r >> 1;
build(l, mid, Ls);
build(mid + 1, r, Rs);
pushup(o);
}
void m1(int p, int l, int r, int o, int v) {
if(l == r) {
t[o].mx += v, t[o].s += v;
return;
}
pushdown(l, r, o);int mid = l + r >> 1;
if(p <= mid) m1(p, l, mid, Ls, v);
else m1(p, mid + 1, r ,Rs, v);
pushup(o);
}
void m2(int L, int R, int l, int r, int o) {
if(L <= l && r <= R) {
md(l, r, o);
return;
}
pushdown(l, r, o);int mid = l + r >> 1;
if(L <= mid) m2(L, R, l, mid, Ls);
if(R > mid) m2(L, R, mid + 1, r, Rs);
pushup(o);
}
data query(int L, int R, int l, int r, int o) {
if(L <= l && r <= R) return t[o];
pushdown(l, r, o); int mid = l + r >> 1;
if(R <= mid) return query(L, R, l, mid, Ls);
if(L > mid) return query(L, R, mid + 1, r, Rs);
return query(L, R, l, mid, Ls) + query(L, R, mid + 1, r, Rs);
}
int qry(int u) {
data res(-inf, 0);
for(; u; u = fa[top[u]]) {
res = query(in[top[u]], in[u], 1, n, 1) + res;
}
return res.mx;
}
int main() {
n = gi(), q = gi();
rep(i, 2, n) Add(gi(), i);
dfs1(1); dfs2(1, 1);
build(1, n, 1);
while(q--) {
int opt = gi(), u = gi();
if(opt == 1) {
m1(in[u], 1, n, 1, 1);
}else if(opt == 2){
int t = qry(u);
m1(in[u], 1, n, 1, -(t + 1));
if(in[u] < out[u])
m2(in[u] + 1, out[u], 1, n, 1);
}else {
int t = qry(u);
puts(t >= 0 ? "black" : "white");
}
}
return 0;
}