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;
}
posted @ 2026-06-08 14:03  Luowj  阅读(8)  评论(0)    收藏  举报