【bzoj 入门OJ】[NOIP 热身赛]Problem C: 星球联盟(并查集)

59 篇文章 0 订阅
10 篇文章 0 订阅

Problem C: 星球联盟

Time Limit:  4 Sec   Memory Limit:  256 MB
Submit:  57   Solved:  15
[
Submit ][ Status ][ Web Board ]

Description

在遥远的S星系中一共有N个星球,编号为1…N。其中的一些星球决定组成联盟,以方便相互间的交流。但是,组成联盟的首要条件就是交通条件。初始时,在这N个星球间有M条太空隧道。每条太空隧道连接两个星球,使得它们能够相互到达。若两个星球属于同一个联盟,则必须存在一条环形线路经过这两个星球,即两个星球间存在两条没有公共隧道的路径。为了壮大联盟的队伍,这些星球将建设P条新的太空隧道。这P条新隧道将按顺序依次建成。一条新轨道建成后,可能会使一些星球属于同一个联盟。你的任务是计算出,在一条新隧道建设完毕后,判断这条新轨道连接的两个星球是否属于同一个联盟,如果属于同一个联盟就计算出这个联盟中有多少个星球。

Input

第1行三个整数N,M和P,分别表示总星球数,初始时太空隧道的数目和即将建设的轨道数目。
第2至第M+1行,每行两个整数,表示初始时的每条太空隧道连接的两个星球编号。
第M+2行至第M+P+1行,每行两个整数,表示新建的太空隧道连接的两个星球编号。
这些太空隧道按照输入的顺序依次建成。
对于10%的数据有1≤N,M,P≤100;
对于40%的数据有1≤N,M,P≤2000;
对于100%的数据有1≤N,M,P≤200000。

Output

输出共P行。如果这条新的太空隧道连接的两个星球属于同一个联盟,就输出一个整数,表示这两个星球所在联盟
的星球数。如果这条新的太空隧道连接的两个星球不属于同一个联盟,就输出"No"(不含引号)。

Sample Input

3 2 1
1 2
1 3
2 3	

Sample Output

3
【样例1说明】
新建成的隧道连接2、3两个星球。
这条隧道1和2,2和3,1和3之间都存在环形线路。1,2,3同属一个联盟,答案为3

HINT

[ Submit][ Status]
【题解】【并查集】
【不明所以,刚开始为什么会“苦思冥想”辣么久...果真zz】

【用并查集维护,离线处理,先用初始的边和询问所加的边建一棵树,把加上就成环的边不要加入 ,记录下来,询问也同理。然后这时有可能是好几棵树,就加一些边把它们并到一棵树里(因为所有可加入的边都已加入,所以不会对结果产生影响)。dfs求出关于树的信息,dep和size,然后再将初始时没有加入的边加入。查询时,遇到的一条边如果是已经加入树的边,就输出No,如果没加入树,就查询并输出】  

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct solve{
	int x,y;
}put[200010],que[200010];
int father[200010],size[200010],deep[200010],fa[200010];
int a[400010],nxt[400010],p[200010],tot;
int n,m,q,cnt,qcnt,num[200010];
inline void add(int x,int y)
{
	tot++; a[tot]=y; nxt[tot]=p[x]; p[x]=tot;
	tot++; a[tot]=x; nxt[tot]=p[y]; p[y]=tot;
}
int find(int x)
{
	if(father[x]==x) return x;
	father[x]=find(father[x]);
	return father[x];
}
void dfs(int x)
{
	deep[x]=deep[fa[x]]+1;
	for(int i=p[x];i!=-1;i=nxt[i])
	 if(a[i]!=fa[x])
       fa[a[i]]=x,dfs(a[i]);
}
void contin(int x,int y)
{
	while(x!=y)
	 {
	 	if(deep[x]<deep[y]) swap(x,y);
	 	int l1=find(x),l2=find(fa[x]);
	 	if(l1!=l2) {father[l1]=l2,size[l2]+=size[l1];}
	 	x=l2;
	 }
}
int main()
{
	freopen("int.txt","r",stdin);
	freopen("my.txt","w",stdout);
	int i,j; cnt=qcnt=tot=0;
	memset(p,-1,sizeof(p));
	memset(nxt,-1,sizeof(nxt));
	scanf("%d%d%d",&n,&m,&q);
    for(i=1;i<=n;++i) father[i]=i;
	for(i=1;i<=m;++i)
	 {
	 	int x,y;
	 	scanf("%d%d",&x,&y);
	 	int l1=find(x),l2=find(y);
	 	if(l1!=l2) father[l2]=l1,add(x,y);
	 	 else put[++cnt].x=x,put[cnt].y=y;
	 }
	for(i=1;i<=q;++i)
	 {
	 	int x,y;
	 	scanf("%d%d",&x,&y);
	 	int l1=find(x),l2=find(y);
	 	if(l1!=l2) father[l2]=l1,add(x,y);
	 	 else que[++qcnt].x=x,que[qcnt].y=y,num[i]=qcnt;
	 }
	for(i=2;i<=n;++i) 
	 {
	 	int l1=find(i),l2=find(1);
	 	if(l1!=l2) add(i,1),father[l1]=l2;
	 }
	for(i=1;i<=n;++i) father[i]=i,size[i]=1;
	dfs(1);
	for(i=1;i<=cnt;++i) 
	   contin(put[i].x,put[i].y);
	for(i=1;i<=q;++i)
	 if(!num[i]) printf("No\n");
	  else 
	   {
	   	contin(que[num[i]].x,que[num[i]].y);
	   	printf("%d\n",size[find(que[num[i]].x)]);
	   }
	return 0;
}


  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值