Problem C: 星球联盟(并查集+lca)

Problem C: 星球联盟

Time Limit: 4 Sec   Memory Limit: 256 MB
Submit: 58   Solved: 16
[ 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]

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#define N 4000003
using namespace std;
int n,m,k,belong[N],deep[N];
int fa[N],point[N],nxt[N],v[N],top[N],size[N],tot,cnt;
struct data
{
	int x,y,opt,ans;
}a[N],b[N];
void add(int x,int y)
{
	tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
	tot++; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
	//cout<<x<<" "<<y<<endl;
}
int find(int x)
{
	if (fa[x]==x) return x;
	fa[x]=find(fa[x]);
	return fa[x];
}
void dfs(int x,int f)
{
	belong[x]=f; deep[x]=deep[f]+1;
	for (int i=point[x];i;i=nxt[i])
	 if (v[i]!=f)
	  dfs(v[i],x);
}
int lca(int x,int y)
{
	x=find(x); y=find(y);
	if (deep[x]<deep[y])  swap(x,y);
	while (find(x)!=find(y))
	 {
	 	int nowx=find(x);
	 	int nowy=find(belong[nowx]);
	 	if (nowy!=nowx)
	 	 size[nowy]+=size[nowx],fa[nowx]=nowy;
	 	x=nowy;
	 	if(deep[x]<deep[y]) swap(x,y);
	 }
	return find(x);
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("my.out","w",stdout);
	scanf("%d%d%d",&k,&n,&m);
	for (int i=1;i<=k;i++) fa[i]=i;
	for (int i=1;i<=n;i++)
	 {
	 	int x,y; scanf("%d%d",&x,&y);
	 	int r1=find(x); int r2=find(y);
	 	if (r1!=r2)
	 	 fa[r2]=r1,add(x,y);
	 	else 
	 	 b[++cnt].x=x,b[cnt].y=y;
	 }
	for (int i=1;i<=m;i++)
	 {
	 	int x,y; scanf("%d%d",&x,&y);
	 	int r1=find(x); int r2=find(y);
	 	if (r1!=r2)
	 	 {
	 	 	fa[r2]=r1; add(x,y);
	 	 	a[i].opt=1;
		  }
		a[i].x=x; a[i].y=y;
	 }
	for (int i=1;i<=k;i++)
	 {
	 	int r1=find(1);
	 	int r2=find(i);
	 	if (r1!=r2)
	 	 {
	 	 	fa[r2]=r1;
	 	 	add(1,i);
		  }
	 }
	dfs(1,0);
	for (int i=1;i<=k;i++)
	 fa[i]=i,size[i]=1;
	for (int i=1;i<=cnt;i++)
	 lca(b[i].x,b[i].y);
	for (int i=1;i<=m;i++)
	 if (a[i].opt!=1)
	  {
	  	int t=lca(a[i].x,a[i].y);
	  	a[i].ans=size[t];
	  }
	for (int i=1;i<=m;i++)
	 if (a[i].opt==1)
	  printf("No\n");
	 else printf("%d\n",a[i].ans);
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值