LG 题解 CF1017G The Tree
写在前面
一道非常好的树链剖分题。
自己为了做这道题,甚至从中提取出了一道题,这个问题貌似很经典。
Solution
先不考虑第二个操作。
对于第一个操作,把它看做单点加,然后对于每个询问 \(x\),就是要找一个 \(1\) 到 \(x\) 路径上的点 \(i\),使得 \(i\) 到 \(x\) 这段路径的权值和大于 \(i\) 和 \(x\) 之间的距离。
这个操作可以用线段树二分维护。
有另外一个思路就是差分,把所有点的权值初始化为 \(-1\),然后询问就变成了找一个 \(i\),使得 \(i\) 到 \(x\) 这段路径的权值和 \(\ge 0\)。
这个信息可以直接维护一个后缀最大值。
考虑 \(2\) 操作,它的本质就是让子树内所有点权变成 \(-1\),但是还不够,因为前面的差分部分还有影响,一个解决办法是在 \(x\) 结点减一个权值 \(v\),不难发现,\(v = Query(x)+1\)。
总复杂度 \(O(n \log^2 n)\),细节看代码。
Code
/*
Work by: Suzt_ilymics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl
using namespace std;
const int MAXN = 1e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;
int n, Q;
int read(){
int s = 0, f = 0;
char ch = getchar();
while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
return f ? -s : s;
}
namespace Seg {
#define lson i << 1
#define rson i << 1 | 1
struct Tree {
int max, sum, lazy, len;
}tree[MAXN << 2];
void Push_up(int i) {
tree[i].max = max(tree[rson].max, tree[rson].sum + tree[lson].max);
tree[i].sum = tree[lson].sum + tree[rson].sum;
}
void Build(int i, int l, int r) {
tree[i].len = r - l + 1;
if(l == r) {
tree[i].max = tree[i].sum = -1;
return ;
}
int mid = (l + r) >> 1;
Build(lson, l, mid), Build(rson, mid + 1, r);
Push_up(i);
}
void Push_down(int i) {
if(!tree[i].lazy) return ;
tree[lson].lazy = -1, tree[rson].lazy = -1;
tree[lson].sum = -tree[lson].len;
tree[rson].sum = -tree[rson].len;
tree[lson].max = -1;
tree[rson].max = -1;
tree[i].lazy = 0;
}
void Modify(int i, int l, int r, int pos, int val) {
if(l == r) {
tree[i].sum += val, tree[i].max += val;
return ;
}
Push_down(i);
int mid = (l + r) >> 1;
if(mid >= pos) Modify(lson, l, mid, pos, val);
else Modify(rson, mid + 1, r, pos, val);
Push_up(i);
}
void Cover(int i, int l, int r, int L, int R) {
if(L <= l && r <= R) {
tree[i].sum = -tree[i].len;
tree[i].max = tree[i].lazy = -1;
return ;
}
Push_down(i);
int mid = (l + r) >> 1;
if(mid >= L) Cover(lson, l, mid, L, R);
if(mid < R) Cover(rson, mid + 1, r, L, R);
Push_up(i);
}
int Query_Sum(int i, int l, int r, int L, int R) {
if(L <= l && r <= R) return tree[i].sum;
Push_down(i);
int mid = (l + r) >> 1, ans = 0;
if(mid >= L) ans += Query_Sum(lson, l, mid, L, R);
if(mid < R) ans += Query_Sum(rson, mid + 1, r, L, R);
return ans;
}
int Query_Max(int i, int l, int r, int L, int R) {
if(L <= l && r <= R) return tree[i].max;
Push_down(i);
int mid = (l + r) >> 1, ans = 0;
bool flag = false;
if(mid >= L) {
ans = Query_Max(lson, l, mid, L, R);
flag = true;
}
if(mid < R) {
int res = Query_Max(rson, mid + 1, r, L, R);
if(flag) ans = max(ans + Query_Sum(rson, mid + 1, r, L, R), res);
else ans = res;
}
return ans;
}
}
namespace Cut {
struct edge { int to, nxt; }e[MAXN << 1];
int head[MAXN], num_edge = 1;
int dep[MAXN], fath[MAXN], siz[MAXN], son[MAXN], dfn[MAXN], top[MAXN], cnt = 0;
void add_edge(int from, int to) { e[++num_edge] = (edge){to, head[from]}, head[from] = num_edge; }
void dfs(int u, int fa) {
dep[u] = dep[fa] + 1, fath[u] = fa, siz[u] = 1;
for(int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if(v == fa) continue;
dfs(v, u);
siz[u] += siz[v];
if(siz[son[u]] < siz[v]) son[u] = v;
}
}
void dfs2(int u, int tp) {
dfn[u] = ++cnt, top[u] = tp;
if(son[u]) dfs2(son[u], tp);
for(int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if(v == fath[u] || v == son[u]) continue;
dfs2(v, v);
}
}
int Query(int x) {
int ans = -INF, sum = 0;
while(top[x] != 1) {
ans = max(ans, Seg::Query_Max(1, 1, n, dfn[top[x]], dfn[x]) + sum);
sum += Seg::Query_Sum(1, 1, n, dfn[top[x]], dfn[x]);
x = fath[top[x]];
}
ans = max(ans, Seg::Query_Max(1, 1, n, dfn[1], dfn[x]) + sum);
return ans;
}
}
int main()
{
n = read(), Q = read();
for(int i = 2, x; i <= n; ++i) x = read(), Cut::add_edge(x, i), Cut::add_edge(i, x);
Cut::dfs(1, 0), Cut::dfs2(1, 1), Seg::Build(1, 1, n);
for(int i = 1, opt, x; i <= Q; ++i) {
opt = read(), x = read();
if(opt == 1) {
Seg::Modify(1, 1, n, Cut::dfn[x], 1);
} else if(opt == 2) {
Seg::Cover(1, 1, n, Cut::dfn[x], Cut::dfn[x] + Cut::siz[x] - 1);
Seg::Modify(1, 1, n, Cut::dfn[x], - Cut::Query(x) - 1);
} else {
int ans = (Cut::Query(x));
// printf("%d ", ans);
puts((ans >= 0) ? "black" : "white");
}
}
return 0;
}