355. 异象石

考点

  • LCA
  • 时间戳

题解

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
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 50, lg = (int)(log2(maxn)) + 1;
queue<int> q;
int n, m, tot, head[maxn], nxt[maxn << 1], ver[maxn << 1], edge[maxn << 1];
// d:到根结点深度
// dis:到根结点距离
ll ans, d[maxn], dis[maxn], f[maxn][lg];
// 时间戳
int cnt, dfn[maxn];
// 按第一维时间戳排序
set<pair<int, int>> s;

void add(int x, int y, int z) {
ver[++tot] = y, nxt[tot] = head[x], edge[tot] = z, head[x] = tot;
}

void bfs() {
d[1] = 1, q.push(1);
while (!q.empty()) {
int x = q.front();
q.pop();
for (int i = head[x]; i; i = nxt[i]) {
int y = ver[i];
if (d[y]) continue;
d[y] = d[x] + 1;
q.push(y);
f[y][0] = x;
for (int j = 1; j < lg; ++j) f[y][j] = f[f[y][j - 1]][j - 1];
}
}
}

int lca(int x, int y) {
if (d[x] < d[y]) swap(x, y);
for (int i = lg - 1; i >= 0; --i) {
if (d[f[x][i]] >= d[y]) x = f[x][i];
}
if (x == y) return x;
for (int i = lg - 1; i >= 0; --i) {
if (f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
}
return f[x][0];
}

ll dist(int x, int y) { return dis[x] + dis[y] - 2 * dis[lca(x, y)]; }

void dfs(int x, int f) {
dfn[x] = ++cnt;
for (int i = head[x]; i; i = nxt[i]) {
int y = ver[i];
if (y == f) continue;
dis[y] = dis[x] + edge[i];
dfs(y, x);
}
}

int pre(int x) {
set<pair<int, int>>::iterator it = s.lower_bound(make_pair(dfn[x], x));
if (it == s.begin()) it = s.end();
return (--it)->second;
}

int suf(int x) {
set<pair<int, int>>::iterator it = s.upper_bound(make_pair(dfn[x], x));
if (it == s.end()) it = s.begin();
return it->second;
}

void insert(int x) {
if (s.size() == 0)
ans = 0;
else if (s.size() == 1)
ans += 2 * dist(s.begin()->second, x);
else {
int l = pre(x), r = suf(x);
ans += dist(l, x) + dist(x, r) - dist(l, r);
}
s.insert({dfn[x], x});
}

void erase(int x) {
s.erase({dfn[x], x});
if (s.size() <= 1)
ans = 0;
else {
int l = pre(x), r = suf(x);
ans -= dist(l, x) + dist(x, r) - dist(l, r);
}
}

int main() {
scanf("%d", &n);
int x, y, z;
for (int i = 1; i < n; ++i) {
scanf("%d%d%d", &x, &y, &z);
add(x, y, z), add(y, x, z);
}
bfs();
dfs(1, 0);
scanf("%d", &m);
char op[2];
while (m--) {
scanf("%s", op);
if (op[0] == '+') {
scanf("%d", &x);
insert(x);
} else if (op[0] == '-') {
scanf("%d", &x);
erase(x);
} else {
printf("%lld\n", ans >> 1);
}
}
return 0;
}

思路

假设有图如下,数字代表DFS序;实际上题目就是问1 2 4 6 8 9构成的子树大小。

可以发现,跟着2 - 4 - 6 - 8 - 9 - 2这个顺序走,路径长度就是这个子树的两倍。

所以用set维护dfs序即可,假设当前的set为空

  • 插入dfs序2,那么ans设为0,因为没有路径
  • 插入dfs序4ans等于2 * dist(2, 4)
  • 插入dfs序6,新增了2 - 64 - 6这两段路径,ans += dist(2, 6) + dist(4, 6) - dist(2, 4)
  • 删除同理不再赘述;最后ans >> 1就是答案