给定一张
n
n
n个点的图,初始没有边,有
q
q
q次操作:
1 x y
:加一条
x
x
x之间的边,边权为
y
y
y。
2 x
:询问从点
x
x
x开始,不经过重复的点,能走到的最远距离。
保证加边的过程中对于任意两个点之间,只有唯一的一条简单路径。
树上一个点距离最远的点有一个性质,就是对每个连通块,我们维护其的一条直径(即距离最远的两个点)
x
x
x,
y
y
y, 如果有多个可以任意维护一个,然后对于点
z
z
z, 连通块内与
z
z
z距离最远的点距离为
m
a
x
(
d
(
x
,
z
)
,
d
(
y
,
z
)
)
max(d(x, z), d(y,z))
max(d(x,z),d(y,z)), 其中
d
(
a
,
b
)
d(a, b)
d(a,b)表示
a
,
b
a, b
a,b的树上距离。这个性质可以通过反证法。
于是我们开一个并查集维护连通块,因为只有加边,所以可以考虑启发式合并,当连通两个点时,将小的连通块合并进大的中,并且更新直径。直径的端点只可能是原连通块直径的端点(共
4
4
4个点).
这里需要一个支持加边和查询两点距离的数据结构,可以使用启发式合并的倍增表,这部分时间复杂度为
O
(
n
log
2
n
)
O(n\log^2n)
O(nlog2n).
总时间复杂度
O
(
n
log
2
n
+
m
log
n
)
O(n\log^2n + m\log n)
O(nlog2n+mlogn).
代码如下:
#include <bits/stdc++.h>
using namespace std;
inline int read() {
int x = 0, f = 0; char ch = getchar();
while (!isdigit(ch)) f = ch == '-', ch = getchar();
while (isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
return f ? -x : x;
}
inline void write(int x) {
if (x < 10) putchar(x + '0');
else write(x / 10), putchar(x % 10 + '0');
}
const int N = 3e5 + 10, M = 6e5 + 10;
int head[N], nex[M], ver[M], tot = 1;
int type, n, q;
int rt[N], siz[N], s[N], t[N];
int dep[N], fa[N][20], d[N];
void add(int x, int y) {
ver[++tot] = y; nex[tot] = head[x]; head[x] = tot;
ver[++tot] = x; nex[tot] = head[y]; head[y] = tot;
}
int find(int x) { return rt[x] == x ? x : rt[x] = find(rt[x]); }
void dfs(int x, int father) {
dep[x] = dep[father] + 1;
fa[x][0] = father;
for (int j = 1; j < 20; ++j) {
fa[x][j] = fa[fa[x][j - 1]][j - 1];
}
for (int i = head[x]; i; i = nex[i]) {
int y = ver[i];
if (y == father) continue;
dfs(y, x);
}
}
int lca(int x, int y) {
if (dep[x] < dep[y]) swap(x, y); // dep[x] >= dep[y];
int depth = dep[x] - dep[y];
for (int j = 19; j >= 0; --j) {
if ((depth >> j) & 1) {
x = fa[x][j];
}
}
if (x == y) return x;
for (int j = 19; j >= 0; --j) {
if (fa[x][j] != fa[y][j]) {
x = fa[x][j]; y = fa[y][j];
}
}
return fa[x][0];
}
int dist(int x, int y) {
int p = lca(x, y);
return dep[x] + dep[y] - dep[p] * 2;
}
void link(int x, int y) {
add(x, y); dfs(y, x);
}
int dfs_d(int x, int la) {
int res = x;
for (int i = head[x]; i; i = nex[i]) {
if (i == (la ^ 1)) continue;
int y = ver[i]; d[y] = d[x] + 1; int v = dfs_d(y, i);
if (d[v] > d[res]) res = v;
}
return res;
}
void upd(int& ss, int& tt, int x, int y) {
if (dist(x, y) > dist(ss, tt)) ss = x, tt = y;
}
void update(int x, int y) {
// d[x] = 1; s[x] = dfs_d(x, 0);
// d[s[x]] = 1; t[x] = dfs_d(s[x], 0);
int a = s[x], b = t[x], c = s[y], d = t[y];
upd(s[x], t[x], c, d);
upd(s[x], t[x], a, c); upd(s[x], t[x], a, d);
upd(s[x], t[x], b, c); upd(s[x], t[x], b, d);
}
void merge(int x, int y) {
int rx = find(x), ry = find(y);
if (siz[rx] < siz[ry]) swap(x, y), swap(rx, ry); // siz[rx] >= siz[ry], y接在x上.
link(x, y); rt[ry] = rx; siz[rx] += siz[ry]; update(rx, ry);
}
int solve(int x) {
int rx = find(x);
return max(dist(x, s[rx]), dist(x, t[rx]));
}
int main() {
// freopen("sample2.in", "r", stdin); freopen("sample2.out", "w", stdout);
type = read(); n = read(); q = read();
for (int i = 1; i <= n; ++i) rt[i] = s[i] = t[i] = i, siz[i] = dep[i] = 1;
int la = 0;
while (q--) {
int opt = read(), x = read(), y;
if (opt == 1) y = read();
if (type) x ^= la, y ^= la;
if (opt == 1) merge(x, y);
else la = solve(x), write(la), putchar('\n');
}
return 0;
}