Description
Input
Output
Sample Input
input | output |
---|---|
4 4 1 2 2 3 1 3 3 4 3 2 4 3 | 1 2 3 |
3 1 1 2 1 1 | 3 |
Sample Output
Hint
题目大意:
n个点m个边
接下来m行 第i条边是从u到v
一个数q表示要依次删去共q条边
编号分别为什么
问 每次删掉这个边之后 这个图有几个连通分支?
解题思路
删边并不好考虑,考虑加边,如果这个边的两个顶点分别属于两个连通分支,那么他们两个相加就导致图中的连通分枝数就少1
那么我们倒着考虑,以例一为例
首先我们把不需要删除的边做一次并查集,这样相当于所有要删的边全删去后的图的连通分支数已经得到了
再加入3号边,就相当于原图删去 2号 4号边 之后有几个连通分支
再加入4号边,相当于原图删去 2号边之后有几个连通分支
2号边就不需要加入了,因为加入2号边相当于没删边有几个连通分支,题目没问。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100005;
struct edge{
int u,v;
}e[maxn];///边编号是1~m
bool ife[maxn];///这个边是不是要被删除
int de[maxn];///删边的顺序
int fa[maxn];
int result[maxn];
void init(){
for(int i=0;i<maxn;++i)
fa[i] = i;
}
int findp(int x){
int xp = x;
while(fa[xp] != xp){
xp = fa[xp];
}
int now = x,newx;
while(fa[now] != xp){
newx = fa[now];
fa[now] = xp;
now = newx;
}
return xp;
}
int main()
{
init();
int n,m,q;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i){
scanf("%d%d",&e[i].u,&e[i].v);
}
scanf("%d",&q);
for(int i=1;i<=q;++i){
scanf("%d",&de[i]);
ife[de[i]] = 1;
}
int ans = n;
for(int i = 1;i<=m;++i){
if(ife[i]) continue;///如果这条边会被删掉,就不加进去
int uf = findp(e[i].u);
int vf = findp(e[i].v);
if(uf!=vf){
fa[uf]=fa[vf];
ans--;
}
}
int j = 1;
result[j++] = ans;
for(int i=q;i>1;--i){///从要删的最后一条边开始加入,第一个边不用加
int uf = findp(e[de[i]].u);
int vf = findp(e[de[i]].v);
if(uf!=vf){///如果两个点的父亲不一样,那么将他们连起来之后,图的连通分支数就会减一
fa[uf]=fa[vf];
ans--;
}
result[j++] = ans;
}
for(int i = j-1;i>0;--i){
printf("%d",result[i]);
if(i!=1) putchar(' ');
}
return 0;
}