CEOI2019 动态直径

题意

给定一棵树,边权带修,强制在线,询问直径。

其中 $3 \le n, q \le 10^5$。

分析

首先,给出一个引理:树上的两个点集 $A,B$,点集 $A$ 的某一条直径端点 $a_1,a_2$,点集 $B$ 的某一条直径端点 $b_1,b_2$,则两个点集之间的直径一定是这 $4$ 个点中最长的距离。反证法,容易证明。

树状数组和 LCA 解决带修路径长度。线段树维护区间的直径和直径端点。

对于修改操作,先修改边权,然后线段树上重构可能跨过这条边的线段树区间直径。

记修改边的深度大的结点为 $u$,注意到 $u$ 的 dfs 序区间内的直径一定不变,以及外部的区间一定不变,这个过程等价于在线段树上 dfs 了 $u$ 的 dfs 序区间,重构一下经过的所有结点。

时间复杂度 $O(n\log n + q \log ^2 n)$。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <string>
#include <utility>
#ifdef XLor
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << std::endl; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
#define dbg(...)
#endif
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
#define ms(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
typedef pair<int,ll> PII;
const int mod = 998244353;
const int inf = 1 << 30;
const int maxn = 100000 + 5;

struct Edge {
int u, v; ll w;
} eg[maxn];

vector<PII> edge[maxn]; ll mw;
int n, q, tin[maxn], tout[maxn], rid[maxn], tot;

namespace hld {
struct BIT {
ll a[maxn];
inline int lowbit(int x) {
return x & -x;
}
inline void update(int i, ll x) {
for (; i <= n; i += lowbit(i)) a[i] += x;
}
inline void update(int l, int r, ll x) {
update(l, x); update(r + 1, -x);
}
inline ll query(int i) {
ll r = 0;
for (; i; i -= lowbit(i)) r += a[i];
return r;
}
} g;

int siz[maxn], dep[maxn], fa[maxn], son[maxn], top[maxn];
void dfs(int u, int f) {
tin[u] = ++tot; rid[tot] = u;
dep[u] = dep[f] + 1; fa[u] = f; siz[u] = 1; son[u] = 0;
int m = -1;
for (auto& x: edge[u]) {
int v = x.first;
if (v == f) continue;
dfs(v, u);
g.update(tin[v], tout[v], x.second);
siz[u] += siz[v];
if (siz[v] > m) son[u] = v, m = siz[v];
}
tout[u] = tot;
}
void dfs(int u, int f, int tp) {
top[u] = tp;
if (!son[u]) return;
dfs(son[u], u, tp); // !
for (auto& x: edge[u]) {
int v = x.first;
if (v == f || v == son[u]) continue; // !
dfs(v, u, v);
}
}
void build() {
dfs(1, 0); dfs(1, 0, 1);
}
int qlca(int u, int v) {
while (top[u] != top[v]){
if (dep[top[u]] < dep[top[v]]) swap(u, v);
u = fa[top[u]];
}
return dep[u] < dep[v] ? u : v;
}
ll qdis(int u, int v) {
ll r = g.query(tin[u]) + g.query(tin[v]);
int l = qlca(u, v);
return r - 2ll * g.query(tin[l]);
}
}
using hld::qdis;

struct Node {
int u, v; ll d;
} a[maxn << 2];
void pushup(int rt) {
if (a[rt << 1].d > a[rt << 1 | 1].d) {
a[rt] = a[rt << 1];
} else {
a[rt] = a[rt << 1 | 1];
}
int x = a[rt << 1].u, y = a[rt << 1].v;
int u = a[rt << 1 | 1].u, v = a[rt << 1 | 1].v;
ll tot;
if ((tot = qdis(x, u)) > a[rt].d) a[rt] = (Node){ x, u, tot };
if ((tot = qdis(x, v)) > a[rt].d) a[rt] = (Node){ x, v, tot };
if ((tot = qdis(y, u)) > a[rt].d) a[rt] = (Node){ y, u, tot };
if ((tot = qdis(y, v)) > a[rt].d) a[rt] = (Node){ y, v, tot };
}
void build(int l = 1, int r = n, int rt = 1) {
if (l == r) {
int u = rid[l];
a[rt] = (Node){ u, u, 0 };
return ;
}
int m = (l + r) / 2;
build(lson); build(rson);
pushup(rt);
}
void update(int L, int R, int l = 1, int r = n, int rt = 1) {
if (L <= l && r <= R) return ;
int m = (l + r) / 2;
if (L <= m) update(L, R, lson);
if (R > m) update(L, R, rson);
pushup(rt);
}

int main() {
scanf("%d%d%lld", &n, &q, &mw);
for (int i = 1; i < n; i++) {
scanf("%d%d%lld", &eg[i].u, &eg[i].v, &eg[i].w);
edge[eg[i].u].push_back({ eg[i].v, eg[i].w });
edge[eg[i].v].push_back({ eg[i].u, eg[i].w });
}
hld::build(); build();
ll last = 0, w;
for (int i = 1, e; i <= q; i++) {
scanf("%d%lld", &e, &w);
e = (e + last) % (n - 1) + 1;
w = (w + last) % mw;
int u = eg[e].u, v = eg[e].v, ww = eg[e].w;
if (hld::fa[v] != u) swap(u, v);
hld::g.update(tin[v], tout[v], w - ww);
eg[e].w = w;
update(tin[v], tout[v]);
printf("%lld\n", last = a[1].d);
}
return 0;
}