题目链接:D-City
解析:一般的思路应该是先将任意两点之间不在询问中的各边连起来,然后再按照倒序将询问的边一个一个加入并查集,同时记录联通块数即可。但是,这样试了一下,老是MLE和TLE,这题能卡住这种常规的做法。因为题意没这么复杂。。。
仔细读题才发现:最后会将所有的边都删去,也就是说,最后一定是n个联通块,而且所有边都删除之后,则倒着操作的起始并查集里没有有一条边。然后就可以向前递推了:如果,即将加进去的边两端点在同一集合,则加进去也不会减少联通块数;否则,联通块数必将减少一个。
AC代码:
#include <cstdio>
#include <iostream>
#include <stack>
#include <vector>
using namespace std;
int pa[10001];
int _find(int x){
return x == pa[x] ? x : pa[x] = _find(pa[x]);
}
void unin(int x, int y){
x = _find(x);
y = _find(y);
if(x != y){
pa[x] = y;
}
}
int n, m;
pair<int, int> edge[100001];
int ans[100001];
int main(){
// freopen("in.txt", "r", stdin);
int x, y;
while(scanf("%d%d", &n, &m) == 2){
for(int i=0; i<n; i++) pa[i] = i;
for(int i=0; i<m; i++){
scanf("%d%d", &edge[i].first, &edge[i].second);
}
ans[m-1] = n; //最后一个是n
for(int i=m-1; i>=0; i--){ //倒着操作
int t = _find(edge[i].first);
int tt = _find(edge[i].second);
if(t == tt) ans[i-1] = ans[i]; //在同一集合
else{
ans[i-1] = ans[i] - 1; //不在同一集合
pa[t] = tt;
}
}
for(int i=0; i<m; i++){
printf("%d\n", ans[i]);
}
}
return 0;
}