2023-6-15
D. Connected Components
题意:
给你 \(n\) 个点 \(m\) 条无向边,\(k\) 次询问,每次给你 \(l,r\) ,问你吧所有编号从 \([l,r]\) 的边删除之后
图里面还有几个连通块?
数据范围:
\(1\leq n \leq 500,1\leq m\leq 2*10^4,1\leq k \leq 2*10^4\)
\(Tutorial:\)
首先对于多组询问,查询一段区间的信息的,我们可以想到前后缀分解,我们可以开 \(m\) 个前后缀并查集维护
只用\([1,i]\) 或者 \([i+1,n]\) ,这些边并查集的合并情况,最后查询的时候我们在用 \(O(n)\) 复杂度去合并
\([1,l-1]\) 和 \([r+1,n]\) 的信息即可,总体复杂度就是 \(O(m*n+k*n)\)
\(Code:\)
#include <bits/stdc++.h>
using namespace std;
const int M = 1e4 + 10, N = 510;
int pre[M][N], suf[M][N];
array<int ,2> edge[M];
int f[N];
int find(int x,int *p) {
return x == p[x] ? x : p[x] = find(p[x], p);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
for (int i = 1; i <= m; i++) {
auto &[x, y] = edge[i];
cin >> x >> y;
}
for (int i = 1; i <= n; i++) {
pre[0][i] = i;
suf[m + 1][i] = i;
}
for (int i = 1; i <= m; i++) {
auto [x, y] = edge[i];
for (int j = 1; j <= n; j++) {
pre[i][j] = pre[i - 1][j];
}
pre[i][find(x, pre[i])] = pre[i][find(y, pre[i])];
}
for (int i = m; i >= 1; i--) {
auto [x, y] = edge[i];
for (int j = 1; j <= n; j++) {
suf[i][j] = suf[i + 1][j];
}
suf[i][find(x, suf[i])] = suf[i][find(y, suf[i])];
}
int k;
cin >> k;
while (k--) {
int l, r;
cin >> l >> r;
for (int i = 1; i <= n; i++) {
f[i] = i;
}
for (int i = 1; i <= n; i++) {
f[find(i, f)] = find(find(i, pre[l - 1]), f);
f[find(i, f)] = find(find(i, suf[r + 1]), f);
}
int res = 0;
for (int i = 1; i <= n; i++) {
res += (f[i] == i);
}
cout << res << "\n";
}
return 0;
}