Tree Restoration Gym - 101755F (并查集)

本文深入探讨了如何从给定的顶点集合及其后代信息中重构一棵树的问题,包括检查环的存在、确保单一树结构、避免自引用以及保证数据完整性等关键步骤。通过详细解析算法流程,提供了一种有效的方法来验证和构建树形结构。

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

 

There is a tree of n vertices. For each vertex a list of all its successors is known (not only direct ones). It is required to restore the tree or to say there is no such tree.

Input

The first line contains a single integer n (1 ≤ n ≤ 1000) — the number of vertices in the tree.

Each of the next n lines contains an integer ci (0 ≤ ci ≤ n) — the number of successors of vertex i, and then ci distinct integers aij (1 ≤ aij ≤ n) — the indices of successors of vertex i.

Output

If the answer does not exist, output «NO».

Otherwise, in the first line output «YES», and then output n - 1 lines containing two integers each — indices of parent and child. Pairs (parent, child) can be output in any order.

Examples

Input
5
4 2 3 4 5
3 3 4 5
2 4 5
1 5
0
Output
YES
1 2
2 3
3 4
4 5
Input
5
4 2 3 4 5
3 3 4 5
0
1 5
0
Output
YES
1 2
2 3
2 4
4 5
Input
3
3 2 3 1
3 3 1 2
3 1 2 3
Output
NO

细节太多,大致先按子孙的数量排序。。。
还是请看我的代码吧,写这篇博客,主要是提醒一下坑点。
1.有环
2.建出来好几课树
3.自己不能当自己的儿子。
4.每个儿子都不能漏
数据一组:

7
5 2 3 4 5 6
2 4 5
2 6 7
0
0
0
0

 

还要注意并查集不能压缩路径

#include<iostream>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#define fuck(x) cout<<#x<<" = "<<x<<endl;
#define ls (t<<1)
#define rs ((t<<1)+1)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 1024;
const int inf = 2.1e9;
const ll Inf = 999999999999999999;
const int mod = 1000000007;
const double eps = 1e-6;
const double pi = acos(-1);
bool vis[maxn];
int num[maxn];
int f[maxn];
struct node
{
    int id,sz;
}a[maxn];
vector<int>v[maxn];
int n,m;

bool cmp(node a,node b){
    return a.sz<b.sz;
}

int mx=1001;
int getf(int x,int t){
    if(t==mx){return -1;}
    if(x==f[x])return x;
    return getf(f[x],t+1);
}

int get_num(int x,int t){
    if(t==mx){return -1;}
    num[x]++;
    if(x==f[x]){return x;}
    return get_num(f[x],t+1);
}

vector<int>tt;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&m);f[i]=i;
        for(int j=1;j<=m;j++){
            int x;
            scanf("%d",&x);
            v[i].push_back(x);
            a[i].sz++;
        }
        a[i].id=i;
    }

    sort(a+1,a+1+n,cmp);
    bool flag=true;
    for(int i=1;i<=n;i++){
        int siz = a[i].sz;
        int id=a[i].id;
        for(int j=0;j<siz;j++){
            vis[v[id][j]]=true;
        }
        vis[id]=true;
        for(int j=0;j<siz;j++){
            int t1=getf(v[id][j],0);
            if(id==v[id][j]||t1==-1||!vis[t1]){flag=false;break;}
            if(t1==v[id][j]){tt.push_back(t1);}
        }
        int sz=tt.size();
        for(int j=0;j<sz;j++){
            f[tt[j]]=id;
        }
        tt.clear();
        if(!flag){break;}
        memset(vis,0,sizeof(vis));
    }

    int rec=0;
    for(int i=1;i<=n;i++){
        if(f[i]==i){rec++;}
        get_num(i,0);
    }
    for(int i=1;i<=n;i++){
        if(num[i]!=v[i].size()+1){flag=false;}
    }
    if(rec!=1){flag=false;}

    if(flag){
        printf("YES\n");
        for(int i=1;i<=n;i++){
            if(f[i]!=i){printf("%d %d\n",f[i],i);}
        }
    }
    else {
        printf("NO\n");
    }

    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/ZGQblogs/p/10542983.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值