题意简述

给你一个 nn 个点的树,mm 次询问,每次询问删除 x,yx,y 两子树后树的重心。

n,m100000n,m \le 100000

题解

一个很烂的正解,别看了

整点阳间的:

记点 uu 的子树大小 sizusiz_u,若 SS 为总点数,若 sizuS2siz_u \le \frac{S}{2} 那么父亲方向上的子树大小为 SsizuS2S-siz_u\ge \frac{S}{2}uu 跳到其父亲更优,那我们要找的点满足 sizu>S2siz_{u}>\frac{S}{2} 。我们在重心的子树中找一个点 uu,跳到的第一个满足要求的点就是重心。

再考虑如何起始点 uu,结论是:我们令 uu 为该树 dfn 序的最中间那个点即可。
证明:一个子树对应 dfn 序上一段区间,考虑中心 xx 必然满足 sizx>S2siz_x>\frac{S}{2} 对应 dfn 序上一段长度大于等于总长一半的区间,必然包含中间点,即中间点 uu 必然在 xx 的子树内。

删子树即在 dfn 序上删除一段区间,删除子树后的 sizsiz 也容易维护。

需要注意的是当有两个重心的时候:

P2

如图,u,vu,v 为重心,uuvv 的父亲,u,vu,v 两边子树大小都为 S2\frac{S}{2}SS 必然是偶数)。此时如果我们把起始点定为 dfn 排序为 S2\frac{S}{2} 的点可能会跳不到 vv 点(当图中红圈中的点在 dfn 序中排 1S21\sim \frac{S}{2} ) ,此时选排名 S2+1\frac{S}{2}+1 的点就可以了(必然在蓝圈里)。

CODE

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
#include <bits/stdc++.h>
template <typename Tp>
inline void read(Tp &x) {
x = 0; bool fg = 0; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') fg ^= 1; ch = getchar();}
while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + (Tp)(ch ^ 48), ch = getchar();
if (fg) x = -x;
}
template <typename Tp, typename... Args>
void read(Tp &t, Args &...args) { read(t); read(args...); }
using namespace std;
typedef long long ll;
const int N = 100010;
const int M = 21;
int head[N], nxt[N << 1], pnt[N << 1], E;
int siz[N], dfL[N], dfR[N], dfn[N], tim;
int fa[M][N];
int n, m;
void adde(int u, int v) {
E++; pnt[E] = v; nxt[E] = head[u]; head[u] = E;
}
void dfs(int u, int f, int d) {
dfL[u] = ++tim; siz[u] = 1; fa[0][u] = f;
dfn[tim] = u;
for (int i = 1; i < M && fa[i - 1][u]; ++i) fa[i][u] = fa[i - 1][fa[i - 1][u]];
for (int i = head[u]; i; i = nxt[i]) {
int v = pnt[i];
if (v == f) continue;
dfs(v, u, d + 1);
siz[u] += siz[v];
}
dfR[u] = tim;
}
bool fa_son(int u, int v) {
return dfL[u] <= dfL[v] && dfR[v] <= dfR[u];
}
int x1, x2;
int getsize(int u) {
return siz[u] - fa_son(u, x1) * siz[x1] - fa_son(u, x2) * siz[x2] +
(fa_son(u, x1) && fa_son(u, x2) && (fa_son(x1, x2) || fa_son(x2, x1))) * min(siz[x1], siz[x2]);
}
int main() {
read(n, m);
for (int i = 1; i < n; ++i) {
int u, v; read(u, v);
adde(u, v); adde(v, u);
}
dfs(1, 0, 0);
// for (int i = 1; i <= n; ++i) {
// printf("%d%c", dfn[i], " \n"[i == n]);
// }
for (int t = 1; t <= m; ++t) {
read(x1, x2);
if (x1 == 1 || x2 == 1) {
puts("0");
continue;
}
int x = -1, tot, mid;
if (fa_son(x1, x2) || fa_son(x2, x1)) {
int L = min(dfL[x1], dfL[x2]), R = max(dfR[x1], dfR[x2]);
tot = n - (R - L + 1); mid = tot / 2 + 1;
if (mid < L) x = dfn[mid];
else x = dfn[R + mid - L + 1];
} else {
int L1 = dfL[x1], R1 = dfR[x1], L2 = dfL[x2], R2 = dfR[x2];
if (L2 < L1) swap(L1, L2), swap(R1, R2);
tot = n - (R1 - L1 + 1) - (R2 - L2 + 1); mid = tot / 2 + 1;
if (mid < L1) x = dfn[mid];
else if (mid <= L1 - 1 + L2 - R1 - 1) x = dfn[R1 + mid - L1 + 1];
else x = dfn[R2 + mid - (L1 + L2 - R1 - 1) + 1];
}
// cerr << "tot = " << tot << " x = " << x << endl;
int a1 = x, a2 = 0;
if (getsize(x) < (tot + 1) / 2) {
for (int i = M - 1 ; i >= 0; --i) {
if (fa[i][x] && getsize(fa[i][x]) < (tot + 1) / 2) x = fa[i][x];
}
a1 = fa[0][x];
}
if (getsize(a1) * 2 == tot) a2 = fa[0][a1];
if (a2) printf("%d %d\n", min(a1, a2), max(a1, a2));
else printf("%d\n", a1);
}
return 0;
}
/*
5 2
1 2
1 3
2 4
2 5
4 5
5 3
*/