P1197 [JSOI2008] 星球大战 解题报告
题意
给你一个有 \(n\) 个节点,\(m\) 条边的图和 \(k\) 个操作,每次操作会删掉图中的一个节点。问每次操作后图中联通块的个数。
\(1\le m \le 2\times 10^5\),\(1\le n \le 2m\)。
思路
正常一个一个删除然后重新建图会超时。
考虑反着来,也就是从最终状态,一个一个合并连通块。
这样就可以使用并查集来求解了。
代码实现中可以给每一条边一条加入的时间方便处理
代码
#include<bits/stdc++.h>
using namespace std;
int n, m, k, cnt, a[400005], memo[400005], ans[400005];
bool f[400005];
vector<int> v[400005];
int find(int x) {
if(memo[x] == x) return x;
return memo[x] = find(memo[x]);
}
void qwq(int x,int y) {
if(find(x) != find(y)) {
memo[find(x)] = find(y);
cnt--;
}
}
signed main() {
cin >> n >> m;
for(int i = 0;i <= n - 1;i ++) {
memo[i] = i;
}
for(int i = 1;i <= m;i ++) {
int x, y;
cin >> x >> y;
v[x].push_back(y);
v[y].push_back(x);
}
cin >> k;
for(int i = 1;i <= k;i ++) {
cin >> a[i];
f[a[i]] = 1;
}
cnt = n - k;
for(int i = 0;i <= n - 1;i ++) {
if(f[i]) continue;
for(int j = 0;j < v[i].size();j ++) {
int x = v[i][j];
if(f[x]) continue;
qwq(i, x);
}
}
ans[k + 1] = cnt;
for(int i = k;i >= 1;i --) {
f[a[i]] = 0;
cnt ++;
for(int j = 0;j < v[a[i]].size();j ++) {
int x = v[a[i]][j];
if(f[x]) continue;
qwq(a[i], x);
}
ans[i] = cnt;
}
for(int i = 1;i <= k + 1;i ++) {
cout << ans[i] << endl;
}
return 0;
}

浙公网安备 33010602011771号