题意描述:给你一个图,有n个点和m条边,然后询问你在删除1条边,2条边,.....m条边以后这个图里有几个连通分量。
分析:询问连通分量,很明显用并查集维护,稍微需要思考的是这个删边操作。熟悉并查集,我们都知道,两个点合并连通分量是很简单的,但是删除边就不能直接操作。
这里我们应该想到逆序,考虑做完所有删边操作以后的图就是我们的初始的图,然后回推这个过程。然后就很容易想到把删改成添的过程,如果添加一条边图中少了一个连通分量,那么删这条边图中很明显就会多一个连通分量。因为要逆序,所以这是一个离线的算法。逆序过程可以用栈来维护。
这题wa了一发,原因是写的是单组输入,改成多组输入立马AC。
#include<stdio.h>
#include<stack>
using namespace std;
const int maxn=1e5+5;
struct edge
{
int u,v;
} e,tem;
int p[10005];
int findset(int x)
{
return x==p[x]?p[x]:p[x]=findset(p[x]);
}
void join(int x,int y)
{
int px=findset(x),py=findset(y);
if(px!=py)
p[py]=px;
}
int main()
{
int n,m,a,b;
while(~scanf("%d%d",&n,&m))
{
stack<edge>st;
stack<int>ans;
for(int i=0; i<n; i++)
p[i]=i;
for(int i=0; i<m; i++)
{
scanf("%d%d",&a,&b);
e.u=a;
e.v=b;
st.push(e);
}
while(!st.empty())
{
tem=st.top();
st.pop();
if(findset(tem.u)!=findset(tem.v))
{
ans.push(n);
n--;
}
else ans.push(n);
join(tem.u,tem.v);
}
while(!ans.empty())
{
printf("%d\n",ans.top());
ans.pop();
}
}
return 0;
}