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");
}
}