CF1017G The Tree
思路
(%%% lby大佬爆切此题)
对于这种能延迟生效的东西,第一考虑就是打标记
先考虑操作 1:我们假设给所有点一个初始点值 \(-1\),当要指定一个点 \(x\) 进行操作 1 时,我们将 \(x\) 的点权加一,因此, \(x\) 的点值实际上代表着它向自己的子树传递的次数
再考虑查询:当询问 \(x\) 的颜色时,我们考虑求出 \(x\) 开始的链上最大后缀 \(suf\),当有 \(suf\ge 0\) 时,代表 \(x\) 就是黑色;反之为白色
最后再考虑操作 2:一个朴素的想法就是将 \(x\) 子树所有点的点权都变回 \(-1\)。但有个问题,因为 \(x\) 的后缀依然有可能 \(\ge 0\)(从它的祖先传递下来的),因此我们要对 \(x\) 的点权减去一个数,实际上就是减去 \(suf(x)+1\),这样 \(x\) 的最大后缀就能保持为 \(-1\)
上面的操作用 树链剖分+线段树(维护区间和、区间最大后缀),可以做到 \(O(n\log^2 n)\)
代码
#include<iostream>
#include<fstream>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#define LL long long
#define FOR(i, x, y) for(int i = (x); i <= (y); i++)
#define ROF(i, x, y) for(int i = (x); i >= (y); i--)
#define PFOR(i, x) for(int i = he[x]; i; i = e[i].nxt)
inline int reads()
{
int sign = 1, re = 0; char c = getchar();
while(c < '0' || c > '9'){if(c == '-') sign = -1; c = getchar();}
while('0' <= c && c <= '9'){re = re * 10 + (c - '0'); c = getchar();}
return sign * re;
}
int n, m;
struct Node
{
int to, nxt;
}e[200005]; int he[100005];
inline void Edge_add(int u, int v)
{
static int cnt = 0;
e[++cnt] = (Node){v, he[u]};
he[u] = cnt;
}
int sz[100005], fa[100005], hs[100005], hr[100005], in[100005], out[100005], icnt;
void dfs1(int now)
{
sz[now] = 1;
PFOR(i, now)
{
int to = e[i].to;
if(to == fa[now]) continue;
fa[to] = now;
dfs1(to);
sz[now] += sz[to];
if(sz[to] > sz[hs[now]]) hs[now] = to;
}
}
void dfs2(int now)
{
in[now] = ++icnt;
if(hs[fa[now]] == now) hr[now] = hr[fa[now]];
else hr[now] = now;
if(hs[now]) dfs2(hs[now]);
PFOR(i, now)
{
int to = e[i].to;
if(to == fa[now] || to == hs[now]) continue;
dfs2(to);
}
out[now] = icnt;
}
namespace Seg_Tree
{
struct Tree
{
int sum, suf;
Tree operator + (Tree b) {return (Tree){sum + b.sum, std::max(b.suf, b.sum + suf)};}
}tr[400005];
bool tag[400005];
#define ls (now << 1)
#define rs ((now << 1) | 1)
inline void up(int now) {tr[now] = tr[ls] + tr[rs];}
inline void down(int now, int l, int r)
{
if(tag[now])
{
int mid = (l + r) >> 1;
tr[ls].suf = tr[rs].suf = -1;
tr[ls].sum = l - mid - 1, tr[rs].sum = mid - r;
tag[ls] = tag[rs] = 1;
tag[now] = 0;
}
}
void build(int now, int l, int r)
{
if(l == r)
{
tr[now] = (Tree){-1, -1};
return;
}
int mid = (l + r) >> 1;
build(ls, l, mid), build(rs, mid + 1, r);
up(now);
}
void modify(int now, int l, int r, int to, int val)
{
if(l == r)
{
tr[now].sum += val, tr[now].suf += val;
return;
}
down(now, l, r);
int mid = (l + r) >> 1;
if(to <= mid) modify(ls, l, mid, to, val);
else modify(rs, mid + 1, r, to, val);
up(now);
}
void cover(int now, int l, int r, int L, int R)
{
if(L <= l && r <= R)
{
tr[now].sum = l - r - 1;
tr[now].suf = -1;
tag[now] = 1;
return;
}
down(now, l, r);
int mid = (l + r) >> 1;
if(L <= mid) cover(ls, l, mid, L, R);
if(mid < R) cover(rs, mid + 1, r, L, R);
up(now);
}
Tree query(int now, int l, int r, int L, int R)
{
if(L <= l && r <= R) return tr[now];
down(now, l, r);
int mid = (l + r) >> 1;
if(R <= mid) return query(ls, l, mid, L, R);
if(mid < L) return query(rs, mid + 1, r, L, R);
return query(ls, l, mid, L, R) + query(rs, mid + 1, r, L, R);
}
inline int max_suf(int now)
{
Tree ans = query(1, 1, n, in[hr[now]], in[now]);
now = fa[hr[now]];
while(now)
{
ans = query(1, 1, n, in[hr[now]], in[now]) + ans;
now = fa[hr[now]];
}
return ans.suf;
}
} using namespace Seg_Tree;
signed main()
{
#ifndef ONLINE_JUDGE
freopen("test.in", "r", stdin);
freopen("test.out", "w", stdout);
#endif
n = reads(), m = reads();
FOR(i, 2, n)
{
int fa = reads();
Edge_add(fa, i), Edge_add(i, fa);
}
dfs1(1), dfs2(1), build(1, 1, n);
FOR(i, 1, m)
{
int ty = reads(), x = reads();
if(ty == 1) modify(1, 1, n, in[x], 1);
if(ty == 2)
cover(1, 1, n, in[x], out[x]),
modify(1, 1, n, in[x], -max_suf(x) - 1);
if(ty == 3) puts(max_suf(x) >= 0 ? "black" : "white");
}
return 0;
}