[CF1017G]The Tree[树链剖分+线段树]

题意

给一棵一开始 \(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;
}
posted @ 2018-12-13 15:44  fwat  阅读(547)  评论(0编辑  收藏  举报