HDU 4496 D-City
D-City
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65535/65535 K (Java/Others)Total Submission(s): 5189 Accepted Submission(s): 1822
题目链接->http://acm.hdu.edu.cn/showproblem.php?pid=4496
Problem Description
Luxer is a really bad guy. He destroys everything he met.
One day Luxer went to D-city. D-city has N D-points and M D-lines. Each D-line connects exactly two D-points. Luxer will destroy all the D-lines. The mayor of D-city wants to know how many connected blocks of D-city left after Luxer destroying the first K D-lines in the input.
Two points are in the same connected blocks if and only if they connect to each other directly or indirectly.
One day Luxer went to D-city. D-city has N D-points and M D-lines. Each D-line connects exactly two D-points. Luxer will destroy all the D-lines. The mayor of D-city wants to know how many connected blocks of D-city left after Luxer destroying the first K D-lines in the input.
Two points are in the same connected blocks if and only if they connect to each other directly or indirectly.
Input
First line of the input contains two integers N and M.
Then following M lines each containing 2 space-separated integers u and v, which denotes an D-line.
Constraints:
0 < N <= 10000
0 < M <= 100000
0 <= u, v < N.
Then following M lines each containing 2 space-separated integers u and v, which denotes an D-line.
Constraints:
0 < N <= 10000
0 < M <= 100000
0 <= u, v < N.
Output
Output M lines, the ith line is the answer after deleting the first i edges in the input.
Sample Input
5 10
0 1
1 2
1 3
1 4
0 2
2 3
0 4
0 3
3 4
2 4
Sample Output
1
1
1
2
2
2
2
3
4
5
Problem Idea
【题意】
题意为:给出一个有N(0<N<=10000)个顶点的无向图(顶点编号0到N-1), 然后依次给出它的M(0<M<=100000)条边,要求当删除已经输入的前k(1<=K<=M)条边后,依次输出该图的连通分量总数。
输入:第一行是N和M,然后是M行数(X,Y)(0<=X,Y<N)表示X与Y有边。
输出:依次输出所求的连通分量数。
【类型】
并查集求无向图的连通分量
【分析】
并查集模板题,入门。
题目所求的“当删除前K条边时图所剩的连通分量数” 就是 “向N个孤立点只添加M-K条边后,所具有的连通分量数”。
所以仅需逆序插入每条边,分别保存插入边后新图所具有的连通分量数目在数组内,然后输出数组即可。
【注意】
findset函数中return的写法要注意:
第一次写模板,找了好长时间发现的问题:
(1)return fa[x]==-1 ? x :fa[x]=findset(fa[x]);而不是return fa[x]==-1 ? x :fa[x]=findset(x);
(2))return fa[x]==-1 ? x :fa[x]=findset(fa[x]);而不是return fa[x]==-1 ? x :findset(fa[x]);
int findset(int x){//返回x节点的根节点,并把x节点直接挂在根节点下面
return fa[x]==-1 ? x :fa[x]=findset(fa[x]);
//如果fa[x]==-1,说明x本身就是根
//如果fa[x]!=-1,递归调用,返回x的父节点所在树的根
}
Souce Code
#include <iostream>
#include <cstring>
#include <vector>
#include <cstdio>
using namespace std;
const int nmax=10000+5;
const int mmax=100000+5;
//并查集基本代码 模板
//fa需要初始化,memeset(fa,-1,sizeof(fa);
int fa[nmax];//fa[i]表示i节点的父节点
int findset(int x){//返回x节点的根节点,并把x节点直接挂在根节点下面
//return fa[x]==-1 ? x :fa[x]=findset(x); 这一行错了,是fa[x]=findset(fa[x]),不是fa[x]=findset(x)
return fa[x]==-1 ? x :fa[x]=findset(fa[x]);//也不能写成return fa[x]==-1?x:findset(fa[x]);
//如果fa[x]==-1,说明x本身就是根
//如果fa[x]!=-1,递归调用,返回x的父节点所在树的根
}
int bind(int u,int v){//合并u节点和v节点所属的连通分量
int fu=findset(u);//获取u节点的根
int fv=findset(v);//获取v节点的根
if(fu!=fv){//若根不同,属于不同的连通分量,则可合并
fa[fu]=fv;
return 1;//连通分量少了1个
}
return 0;//若根相同连通分量数不变
}
int main() {
int n,m;//n个节点,m条边
while(scanf("%d%d",&n,&m)==2){
memset(fa,-1, sizeof(fa));//初始化fa数组
vector<pair<int,int>>vc;//vc按序保存所有的边,记录起点和终点
for(int i=0;i<m;i++){//依次输入各条边
int u,v;//起点和终点
scanf("%d%d",&u,&v);
vc.push_back(make_pair(u,v));
}
vector<int> res;//res数组保存连通分量的数值
int cnt=n;//cnt记录当前的连通分量数
res.push_back(cnt);
//当删除当前已输入的前K条边时图所剩的连通分量数 就是向N个孤立的点只添加M-K条边后,所具有的连通分量数。
for(int i=m-1;i>=1;i--){//逆序依次连接所有孤立的点,边至少一条
cnt-=bind(vc[i].first,vc[i].second);
res.push_back(cnt);
}
for(int i=res.size()-1;i>=0;i--){//节点数最小为0
printf("%d\n",res[i]);
}
}
return 0;
}