题目描述
小 ( ext{L}) 是一位网络爱好者,( ext{TA}) 有 (n) 台能接入网络的设备,编号为 (1,2,dots,n);还有 (m) 条连接它们的线缆,编号为 (1,2,dots,m)。每条线缆都可以在连接的两台设备间双向传输数据。
在建成网络之后,小 ( ext{L}) 开始担心网络的安全问题。( ext{TA}) 希望测试一下这个网络的抗干扰能力,于是准备了 (q) 次测试:给定两个参数 (l_i) 和 (r_i),小 ( ext{L})会询问当所有编号在 (l_i) 和 (r_i) 之间的线缆都断开的时候,整个网络会分成多少块,满足同一块内的任意两台设备都能互相发送数据,而不同块间的任意两台设备都不能互相发送数据。注意测试之间是相互独立的,在某次测试中断开的线缆会在这一次测试结束后立即再连回去。
小 ( ext{L}) 马上开始动手测试,但 ( ext{TA}) 发现工作量实在太大了,自己完不成,于是 ( ext{TA}) 找到了你。请你根据小 ( ext{L}) 给出的网络描述和测试描述,回答每次测试的结果。
输入格式
第一行输入两个正整数 (n) 和 (m),表示网络中的设备数和线缆数。
接下来 (m) 行,第 (i) 行包含两个正整数 (x) 和 (y)((x eq y)),表示编号为 (i) 的线缆连接了编号为 (x) 和 (y) 的设备。两台设备间可能有多于一条线缆。
下一行包含一个正整数 (q),表示测试的个数。接下来 (q) 行,每行两个正整数 (l_i) 和 (r_i),描述一次测试。
输出格式
输出 (q) 行,每行一个正整数,描述对应测试的结果。
样例
1.in
3 4
1 2
2 3
1 3
3 1
3
1 2
2 4
3 4
1.out
2
2
1
2.in
6 5
1 2
5 4
2 3
3 1
3 6
6
1 3
2 5
1 5
5 5
2 4
3 3
2.out
4
5
6
3
4
2
数据范围与约定
对于 (30\%) 的数据,(n leq 50,m,q leq 1000);
对于另外 (30\%) 的数据,只有不超过 (100) 个不同的 (l_i);
对于 (100\%) 的数据,(2 leq n leq 500,1 leq m leq 10^4,1 leq q leq 2 * 10 ^ 4)
题解
并查集。把全部的边从前往后扫一遍,找到能让不连通的两块变得连通的几条边,从后往前也进行一遍。这样我们就找到了从前往后以及从后往前第一条能使 (u) 和 (v) 两个不连通的块变得连通的边,对于每次断边 ([l,r]) ,我们从之前存下的从前往后以及从后往前第一条能使 (u) 和 (v) 两个不连通的块变得连通的边中找到不在 ([l,r]) 中的边用并查集维护一下最后看有多少个块就行了。
Code
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#define MAXN 10001
int n, m, q, u[MAXN], v[MAXN];
int fa[501], size[501];
int pf[MAXN], sf[MAXN];
int find(int x) {
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
void Union(int x, int y) {
int rootx = find(x), rooty = find(y);
if (rootx == rooty) return;
if (size[rootx] > size[rooty]) {
size[rootx] += size[rooty];
fa[rooty] = rootx;
}
else {
size[rooty] += size[rootx];
fa[rootx] = rooty;
}
}
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; ++i) fa[i] = i, size[i] = 0;
for (int i = 1; i <= m; ++i) {
scanf("%d %d", &u[i], &v[i]);
}
for (int i = 1; i <= m; ++i) {
if (find(u[i]) != find(v[i])) {
Union(u[i], v[i]);
pf[++pf[0]] = i;
}
}
for (int i = 1; i <= n; ++i) fa[i] = i, size[i] = 0;
for (int i = m; i >= 1; --i) {
if (find(u[i]) != find(v[i])) {
Union(u[i], v[i]);
sf[++sf[0]] = i;
}
}
scanf("%d", &q);
for (int i = 1, x, y, ans = 0; i <= q; ++i) {
for (int j = 1; j <= n; ++j) fa[j] = j, size[j] = 0;
scanf("%d %d", &x, &y), ans = 0;
for (int j = 1; j <= pf[0] && pf[j] < x; ++j) {
Union(u[pf[j]], v[pf[j]]);
}
for (int j = 1; j <= sf[0] && sf[j] > y; ++j) {
Union(u[sf[j]], v[sf[j]]);
}
for (int j = 1; j <= n; ++j) {
if (fa[j] == j) ++ans;
}
printf("%d
", ans);
}
return 0;
}