P3703 树点涂色 题解

Statement

树,每个点有一个颜色,初始每个点颜色互不相同

  • 到根链涂上新颜色
  • 链颜色数
  • \(u\) 子树内点 \(v\) 到根路径的最多颜色数

Solution

首先,相同颜色的点一定构成一条从下往上的链

考虑 LCT,维护一个性质:一条实链的点的颜色相同.

于是 \(u\) 到根的颜色数 \(=\) \(u\) 到根的虚边数加一,记虚边数为 \(f(u)\).

于是第二个询问,等于 \(f(u)+f(v)-2\cdot f(lca)+1\)

第三个询问,等于 \(\max_{v\in\text{subtree}(u)}f(v)+1\)

如果问“\(u\) 子树内点 \(v\)\(u\) 路径的最多颜色数”,答案等于 \(\max_{v\in\text{subtree}(u)}f(v)-f(u)+1\)

考虑修改操作,我们 access 一下

考虑线段树维护这个 \(f(u)\),操作有:区间加一 / 减一,区间问 \(\max\)

在 access 过程中进行区间加一 / 减一

注意到在 LCT 中设 \(u\) 的父亲为 \(v\)\((u,v)\) 由虚变实、\((w,v)\) 由实变虚,我们需要找到 \(u\) 所在实链的链顶、\(v\) 所在实链的第一个深度比 \(v\) 大的点

所以还要写个 \(\text{find}(u)\) 表示寻找 LCT 中 \(u\) 子树内深度最小的点

或者这样:多维护个信息 left[u] 表示 \(u\) 子树内最左边的点,这个可以在 push_up 时维护

Code

#include <bits/stdc++.h>
using namespace std;
#define rep(i, j, k) for (int i = (j); i <= (k); ++i)
#define reo(i, j, k) for (int i = (j); i >= (k); --i)
typedef long long ll;
const int N = 1e5 + 10, INF = 2e9;
vector<int> G[N];
int n, m;

int tim, dfn[N], revdfn[N], sz[N], dep[N], Fa[N], son[N], Top[N];
void DFS1(int u, int fa) {
	dep[u] = dep[fa] + 1, sz[u] = 1, Fa[u] = fa;
	for (int v : G[u]) 
		if (v != fa) {
			DFS1(v, u), sz[u] += sz[v];
			if (sz[v] > sz[son[u]]) {
				son[u] = v;
			}
		}
}
void DFS2(int u, int tp) {
	dfn[u] = ++tim, revdfn[tim] = u, Top[u] = tp;
	if (son[u]) DFS2(son[u], tp);
	for (int v : G[u])
		if (v != Fa[u] && v != son[u])
			DFS2(v, v);
}
int LCA(int u, int v) {
	while (Top[u] != Top[v]) {
		if (dep[Top[u]] < dep[Top[v]]) {
			v = Fa[Top[v]];
		} else {
			u = Fa[Top[u]];
		}
	}
	return dep[u] < dep[v] ? u : v;
}

namespace SegmentTree {
	int add[N << 2], mx[N << 2];
  #define lc (u << 1)
  #define rc ((u << 1) | 1)
  #define mid ((l + r) >> 1)
	void up(int u) {
		mx[u] = max(mx[lc], mx[rc]);
	}
	void Add(int u, int v) {
		add[u] += v, mx[u] += v;
	}
	void down(int u) {
		if (add[u]) Add(lc, add[u]), Add(rc, add[u]), add[u] = 0;
	}
	void Build(int u, int l, int r) {
		add[u] = 0;
		if (l == r) return (void)(mx[u] = dep[revdfn[l]] - 1);
		Build(lc, l, mid), Build(rc, mid + 1, r), up(u);
	}
	void Upd(int u, int l, int r, int x, int y, int v) {
		if (y < l || r < x || x > y) return;
		if (x <= l && r <= y) return Add(u, v);
		down(u), Upd(lc, l, mid, x, y, v), Upd(rc, mid + 1, r, x, y, v), up(u);
	}
	int Qry(int u, int l, int r, int x, int y) {
		if (y < l || r < x || x > y) return -INF;
		if (x <= l && r <= y) return mx[u];
		return down(u), max(Qry(lc, l, mid, x, y), Qry(rc, mid + 1, r, x, y));
	}
  #undef lc
  #undef rc
  #undef mid
	int Query(int x) {
		return Qry(1, 1, n, x, x);
	}
	int Query(int x, int y) {
		return Qry(1, 1, n, x, y);
	}
}

namespace LinkCutTree {
	int fa[N], ch[N][2], left[N];
  #define get(u) (u == ch[fa[u]][1])
  #define nrt(u) (u == ch[fa[u]][0] || u == ch[fa[u]][1])
	void up(int u) {
		if (ch[u][0]) left[u] = left[ch[u][0]];
		else left[u] = u;
	}
	void rot(int u) {
		int f = fa[u], g = fa[f], k = get(u);
		if (nrt(f)) ch[g][get(f)] = u;
		ch[f][k] = ch[u][!k];
		if (ch[u][!k]) fa[ch[u][!k]] = f;
		ch[u][!k] = f, fa[f] = u, fa[u] = g, up(f), up(u);
	}
	void splay(int u) {
		for (; nrt(u); rot(u)) if (nrt(fa[u])) rot(get(u) == get(fa[u]) ? fa[u] : u);
	}
	void access(int u) {
		using namespace SegmentTree;
		for (int v = 0; u; v = u, u = fa[u]) {
			splay(u);
			if (v) {
				int p = left[v];
				Upd(1, 1, n, dfn[p], dfn[p] + sz[p] - 1, -1);
			}
			if (ch[u][1]) {
				int p = left[ch[u][1]];
				Upd(1, 1, n, dfn[p], dfn[p] + sz[p] - 1, 1);
			}
			ch[u][1] = v, up(u);
		}
	}
  #undef get
  #undef nrt
}

int main() {
	using namespace LinkCutTree;
	using namespace SegmentTree;
	ios::sync_with_stdio(false), cin.tie(nullptr);
	cin >> n >> m;
	rep(i, 1, n - 1) {
		int u, v;
		cin >> u >> v;
		G[u].push_back(v), G[v].push_back(u);
	}
	DFS1(1, 0), DFS2(1, 1), Build(1, 1, n);
	rep(i, 1, n) fa[i] = Fa[i];
	while (m--) {
		int op, x, y;
		cin >> op;
		if (op == 1) {
			cin >> x, access(x);
		}
		if (op == 2) {
			cin >> x >> y;
			cout << Query(dfn[x]) + Query(dfn[y]) - 2 * Query(dfn[LCA(x, y)]) + 1 << '\n';
		}
		if (op == 3) {
			cin >> x;
			cout << Query(dfn[x], dfn[x] + sz[x] - 1) + 1 << '\n';
		}
	}
	return 0;
}
posted @ 2024-09-22 21:47  Laijinyi  阅读(4)  评论(0编辑  收藏  举报