题目大意:
先建出一棵树,预处理出 $dep$ 和 $fa$,然后用一个并查集维护连通性,另一个并查集维护每个点所在的环。缩环的时候每次向上跳 $father$,每跳一步就减少一个点,因此每个点最多被跳到一次,所以总复杂度是 $O((m+q)\log n)$ 的。
#include <cstdio>
const int N = 200005, M = 400005;
struct Node {
int x, y;
} a[M];
int head[N], nxt[M], v[M], siz[N], f1[N], f2[N], fa[N], dep[N], tot;
int read() {
int x = 0; char c = getchar();
while (c < '0' || c > '9') c = getchar();
while (c >= '0' && c <= '9') {
x = (x << 3) + (x << 1) + (c ^ 48);
c = getchar();
}
return x;
}
void swap(int &x, int &y) {
x ^= y, y ^= x, x ^= y;
}
void add(int from, int to) {
nxt[++tot] = head[from];
head[from] = tot, v[tot] = to;
}
int find1(int x) {
if (x != f1[x]) f1[x] = find1(f1[x]);
return f1[x];
}
int find2(int x) {
if (x != f2[x]) f2[x] = find2(f2[x]);
return f2[x];
}
void dfs(int u, int f, int d) {
fa[u] = f, dep[u] = d;
for (int i = head[u]; i; i = nxt[i])
if (v[i] != f) dfs(v[i], u, d + 1);
}
void merge(int x, int y) {
x = find2(x), y = find2(y);
while (x != y) {
if (dep[x] < dep[y]) swap(x, y);
int fx = find2(fa[x]);
f2[x] = fx, siz[fx] += siz[x], x = fx;
}
}
int main() {
int n = read(), m = read(), q = read();
for (int i = 1; i <= n; ++i) f1[i] = i;
for (int i = 1; i <= m + q; ++i) {
int x = read(), y = read();
int fx = find1(x), fy = find1(y);
if (fx != fy) add(x, y), add(y, x), f1[fx] = fy;
a[i] = (Node){x, y};
}
for (int i = 1; i <= n; ++i) if (!dep[i]) dfs(i, 0, 0);
for (int i = 1; i <= n; ++i) f1[i] = f2[i] = i, siz[i] = 1;
for (int i = 1; i <= m + q; ++i) {
int fx = find1(a[i].x), fy = find1(a[i].y);
if (fx != fy) {
f1[fx] = fy;
if (i > m) puts("No");
} else {
merge(a[i].x, a[i].y);
if (i > m) printf("%d\n", siz[find2(a[i].x)]);
}
}
return 0;
}
时间复杂度 $O((m+q)\log n)$