CF1192B Dynamic Diameter 题解
思路
静态 \(\text{top tree}\) 板子题。
定义
我们使用簇来表示树上的一个连通块。
可以按照如下方式定义一个簇:
- 一个簇可以表示为三元组 \((u,v,E)\),其中 \(u,v\) 为树的节点,称为簇的界点,\(E\) 为一个边的集合,表示该簇包含的边,路径 \((u,v)\) 称作簇路径。
- \(u,v\) 分别为上界点与下界点,上界点是一个簇内深度最小的点,下界点则是除去上界点外的界点。
- 界点可以通俗的理解为簇与簇的分界点。
- 对于树的一条边 \((u,v)\),\((u,v,{(u,v)})\) 是一个簇。
- 对于簇 \((u,v,E_1)\) 与簇 \((v,w,E_2)\) 且 \(E_1\cap E_2=\varnothing\),那么 \((u,w,E_1\cup E_2)\) 也是一个簇,这个操作称为 \(\text{compress}\)。
- 对于簇 \((x,v,E_1)\) 与簇 \((x,w,E_2)\) 且 \(E_1\cap E_2=\varnothing\),那么 \((x,w,E_1\cup E_2)\) (或者 \((x,v,E_1\cup E_2)\))也是一个簇,这个操作称为 \(\text{rake}\)。
经典图:
一个感性理解是 \(\text{rake}\) 将簇与簇融合,\(\text{compress}\) 将簇与簇拼接。
这两个操作可以帮助我们将整棵树合并为一个簇。
- 对于一度点,进行 \(\text{rake}\)
- 对于二度点,进行 \(\text{compress}\)。
收缩过程中,簇的合并结构形成了一个树,我们把这个树称为 \(\text{top tree}\)。
现在,这样一个 \(\text{top tree}\) 它的树高有可能是 \(O(n)\) 的。
更优的树
考虑构造一个树高更小的 \(\text{top tree}\)。
容易想到的是全局平衡二叉树。
全局平衡二叉树提供了一个分治方案使整棵树的全局平衡二叉树树高为 \(O(\log n)\)。
我们同样可以把这个分治方案放在 \(\text{top tree}\) 上。
具体的,将树进行重链剖分。
然后对于轻儿子,先把它们 \(\text{rake}\) 起来。
再对重链分治 \(\text{compress}\),找分割点时,我们按每个点合并完轻儿子后的簇的大小带权找到中点。
这样就可以建出一颗树高为 \(O(\log n)\) 的 \(\text{top tree}\) 了。
关于这道题
建出 \(\text{top tree}\) 后有什么用呢。
我们想线段树一样的操作它
对于动态直径。
我们把每一个簇,维护三个值。
簇内部的最长距离及到两个界点的最长距离。
合并时我们对两种操作分类讨论拼接起来即可。
可以发现,维护直径的方式与线段树维护最大子段和的方式是比较类似的。
这种维护方法就可以支持一些简单的修改,比如此题的修改边权。
时间复杂度:\(O(n\log n)\)。
Code
/*
! 如果没有天赋,那就一直重复
! Created: 2024/06/05 19:58:38
*/
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define fro(i, x, y) for (int i = (x); i <= (y); i++)
#define pre(i, x, y) for (int i = (x); i >= (y); i--)
const int N = 2e5 + 10;
int n, q, w, ct, last, head[N];
int sz[N], sn[N], fa[N], rt[N], dep[N];
struct Node {
int u, v, w;
} d[N];
struct edge {
int to, nxt;
} e[N << 1];
struct node {
enum { UNIT, RAKE, COMP } type;
int u, v, sz, ls, rs, fa, ln, mi, un, vn;
} t[N << 1];
inline void add(int x, int y) {
e[++ct] = {y, head[x]}, head[x] = ct;
e[++ct] = {x, head[y]}, head[y] = ct;
}
inline void dfs(int now, int fa) {
sz[now] = 1, dep[now] = dep[fa] + 1;
for (int i = head[now]; i; i = e[i].nxt) {
int x = e[i].to;
if (x == fa) continue;
dfs(x, now);
t[x] = {node::UNIT, now, x, 1};
sz[now] += sz[x];
if (sz[x] > sz[sn[now]]) sn[now] = x;
}
}
inline void pup(int p) {
int x = t[p].ls, y = t[p].rs;
if (t[p].type == node::RAKE) {
t[p].ln = t[x].ln;
t[p].mi = max({t[x].mi, t[y].mi, t[x].un + t[y].un});
t[p].un = max(t[x].un, t[y].un);
t[p].vn = max(t[x].vn, t[x].ln + t[y].un);
} else if (t[p].type == node::COMP) {
t[p].ln = t[x].ln + t[y].ln;
t[p].mi = max({t[x].mi, t[y].mi, t[x].vn + t[y].un});
t[p].un = max(t[x].un, t[x].ln + t[y].un);
t[p].vn = max(t[y].vn, t[y].ln + t[x].vn);
}
}
inline auto rake(int x, int y) {
assert(t[x].u == t[y].u);
return t[++ct] = {node::RAKE, t[x].u, t[x].v, t[x].sz + t[y].sz, x, y}, t[x].fa = t[y].fa = ct, pup(ct), ct;
}
inline auto comp(int x, int y) {
assert(t[x].v == t[y].u);
return t[++ct] = {node::COMP, t[x].u, t[y].v, t[x].sz + t[y].sz, x, y}, t[x].fa = t[y].fa = ct, pup(ct), ct;
}
template<typename T, typename Func>
inline int build(T l, T r, Func f) {
if (r == l) return 0;
if (r == l + 1) return *l;
int all = 0, sum = 0;
for (auto it = l; it != r; it++) all += t[*it].sz;
T mid = l + 1;
for (auto it = l; it != r; it++) {
sum += t[*it].sz;
if (sum <= all / 2) mid = it + 1; else break;
}
return f(build(l, mid, f), build(mid, r, f));
}
inline void sol(int now, int ff, bool f) {
fa[now] = ff;
if (sn[now]) sol(sn[now], now, false);
for (int i = head[now]; i; i = e[i].nxt)
if (e[i].to != sn[now] && e[i].to != fa[now]) sol(e[i].to, now, true);
if (f) {
vector<int> s1;
if (ff) s1.push_back(now);
for (int i = sn[now]; i; i = sn[i]) {
vector<int> s2{i};
for (int j = head[fa[i]]; j; j = e[j].nxt)
if (e[j].to != i && e[j].to != fa[fa[i]]) s2.push_back(rt[e[j].to]);
s1.push_back(build(s2.begin(), s2.end(), rake));
}
rt[now] = build(s1.begin(), s1.end(), comp);
}
}
inline void upd(int x, int w) {
t[x].un = t[x].vn = t[x].mi = t[x].ln = w;
while (t[x].fa) {
pup(t[x].fa), x = t[x].fa;
}
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> q >> w;
fro(i, 1, n - 1) {
cin >> d[i].u >> d[i].v >> d[i].w;
add(d[i].u, d[i].v);
}
ct = n;
dfs(1, 0);
sol(1, 0, 1);
fro(i, 1, n - 1) {
if (dep[d[i].u] > dep[d[i].v])
swap(d[i].u, d[i].v);
upd(d[i].v, d[i].w);
}
fro(i, 1, q) {
int x, y;
cin >> x >> y;
x = (x + last) % (n - 1) + 1;
y = (y + last) % (w);
d[x].w = y;
upd(d[x].v, d[x].w);
cout << (last = t[rt[1]].mi) << "\n";
}
return 0;
}