gym100513 K Treeland

本文解析了Codeforces上一道关于树构造的题目,通过使用并查集和队列维护算法,有效地解决了如何从给定的节点连接信息中构建一棵树的问题。详细介绍了算法思路,包括如何判断边的有效性及如何确保生成的边构成无环树。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

https://codeforces.com/gym/100513/problem/K

我们知道对于每天相连的边,他们的距离都是1,而每一个C(i,1)=i,C(i,2)=C(i,....)=1就是i直接连的边

我们记录每个点已经被哪些边连上了,也就是每个点当前的度+1是多少l[i],那么对于每一个Ci,还没考虑的边的下标就是l[i]+1,对于一条边(u,v),如果a[u][l[u]+1]=v,a[v][l[v]+1]=u,说明u-v这条边是连起来的

用一个队列维护,由于直接相连的边都在C(i)的靠左边,那么先进后出的顺序,先n-1条入队的边就是这棵树的一个可行解。

对于每时每刻肯定会有一条边可以入队的证明,需要感性地认识到树是没有环的,没有环就不会队列进行不下去

注意用并查集判断一下连过去的边是否已经在同一个连通块里

#include<bits/stdc++.h>
using namespace std;
int a[2005][2005],l[2005],d[2005][2005],vis[2005][2005];
int fa[2005];
struct Edge{
	int u,v;
};
int fid(int x){
	return fa[x]==x?x:fa[x]=fid(fa[x]);
}
int mrge(int x,int y){
	int fx=fid(x),fy=fid(y);
//	printf("%d %d\n",fx,fy);
	if(fx==fy)return 0;
	fa[fx]=fy;
	return 1;
}
int main(){
	int t,n=0;
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		for(int i=1;i<=n;i++)l[i]=1,fa[i]=i;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				scanf("%d",&a[i][j]);
				d[i][a[i][j]]=j;
			}
		}
		queue<Edge>q;
		for(int i=1;i<=n;i++){
			for(int j=1;j<i;j++){
				if(d[i][j]==2&&d[j][i]==2){
					Edge t;
					t.u=i,t.v=j;
					if(!mrge(i,j))continue;
					q.push(t);
				}
			}
		}
		int tot=1;
		while(!q.empty()&&tot<n){
			Edge t=q.front();q.pop();
			printf("%d %d\n",t.u,t.v);
			tot++;
			l[t.u]++,l[t.v]++;
			for(int i=1;i<=n;i++){
				if(d[t.u][i]==l[t.u]+1&&d[i][t.u]==l[i]+1){
					Edge k;
					k.u=t.u,k.v=i;
					if(!mrge(k.u,k.v))continue;
					q.push(k);
				}
				if(d[t.v][i]==l[t.v]+1&&d[i][t.v]==l[i]+1){
					Edge k;
					k.u=t.v,k.v=i;
					if(!mrge(k.u,k.v))continue;
					q.push(k);
				}
			}
		}
		printf("\n");
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值