题面:
样例输入:
6 7
1 2
1 4
1 5
2 4
2 3
3 5
3 6
样例输出:
1
2
3
2
1
0
题意:
给你一个无向图,n个点,m条边。点的编号1—n。
现在按照1-n顺序每次删掉一个点 请问删完后还剩多少个连通块。
题解:
比赛的时候本来一直在想用tarjan求出每个点相连的桥。然后减一下。后来越想越麻烦。其实不用这么求。我们只用从后往前遍历删除的点,因为输出是从前往后输出,所以在遍历到第i个点时,它前面的i-1个点是不需要考虑的。我们就可以用并查集,从后往前加点进入图中,如果第i个点和比他大点有连边,并且他们的祖宗值不一样,那我们的连通块就要减一。最后将答案倒着输出。
ps:并查集需要路压,不然会超时。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5,M=2e5+5;
vector<int>G[N];
int pre[N];
int findx(int x)
{
return x==pre[x]?x:pre[x]=findx(pre[x]);//路径压缩
}
bool merge(int u,int v)
{
int tu=findx(u),tv=findx(v);
if(tu!=tv)
{
pre[tv]=tu;
return 1;
}
return 0;
}
int main()
{
int n,m;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)pre[i]=i;
while(m--)
{
int a,b;
scanf("%d %d",&a,&b);
G[a].push_back(b);
G[b].push_back(a);
}
int ans=0;
vector<int>res;
res.push_back(0);
for(int u=n;u>=2;u--)
{
ans++;
for(auto v:G[u])
{
if(v>u&&merge(u,v))ans--;//如果相连的点比他大,并且不是同一个祖宗,联通块就减一。
}
res.push_back(ans);
}
reverse(res.begin(),res.end());
for(auto i:res)
{
printf("%d\n",i);
}
}