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;
}
标签:dep,P3703,题解,void,son,int,add,涂色,Top
From: https://www.cnblogs.com/laijinyi/p/18425956