水影若深蓝(并查集+思维+构造)

108 篇文章 0 订阅

水影若深蓝

题目背景

帆帆爱明日方舟,更爱水月和深蓝之树。但在龙年,他想构建一棵独属于他的深蓝之树。

题目描述

在梦里,在朦胧的水影中,帆帆看见了这棵只属于他的深蓝之树。

当一切梦沉寂,他却遗忘了所有,只记得树上有 n n n 个结点。

真的吗?似乎并没有,有 m m m 件事情他一直牢记着,第 i i i 件事情是:节点 u i u_i ui v i v_i vi 在这棵深蓝之树上唯一的不重复经过某个点的路径恰好有两条边。

那么,你能否帮助帆帆想起来这棵深蓝之树的样子呢,如果有多个可能的,你只需要输出任意一种。

当然,帆帆也是会磨损的,因此当你发现无论如何都找不到满足帆帆这 m m m 件事情的深蓝之树时,你需要报告无解。

输入格式

本题有多组数据

第一行一个正整数 T T T 表示数据组数。

对于每组数据:

第一行两个非负整数 n , m n,m n,m 表示树上的点数和帆帆记忆中事件个数。

接下来 m m m 行,每行两个正整数 u i , v i u_i,v_i ui,vi 表示一个事件。

输出格式

对于每组数据:

  • 若不存在符合条件的树,输出 No
  • 否则,第一行输出 Yes,接下来 n − 1 n-1 n1 行输出 n − 1 n-1 n1 对正整数 ( u , v ) (u,v) (u,v),表示你给出的树上的 n − 1 n-1 n1 条边。

本题采用 special judge 评测,也就是说,如果有多种可能的答案,你可以输出任意一种。

样例 #1

样例输入 #1

2
4 2
1 2
3 4
3 3
1 2
2 3
3 1

样例输出 #1

Yes
1 3
2 4
2 3
No

提示

样例 1 1 1 解释

对于第一组数据,不难验证给出的树符合条件。

对于第二组数据,可以证明不存在符合条件的树。

测试点约束

对于 100 % 100\% 100% 的数据, 1 ≤ T ≤ 1 0 5 1\le T\le 10^5 1T105 n ≥ 1 n\ge 1 n1 m ≥ 0 m\ge 0 m0 1 ≤ ∑ n ≤ 3 × 1 0 5 1\le \sum n\le 3\times 10^5 1n3×105 0 ≤ ∑ m ≤ 3 × 1 0 5 0\le \sum m\le 3\times 10^5 0m3×105 1 ≤ u i , v i ≤ n 1\le u_i,v_i\le n 1ui,vin u i ≠ v i u_i\neq v_i ui=vi

本题采用捆绑测试。

子任务编号特殊限制分值
Subtask #1 ∑ n , ∑ m ≤ 9 \sum n,\sum m\le 9 n,m9 10 10 10
Subtask #2 ∑ n , ∑ m ≤ 50 \sum n,\sum m\le 50 n,m50 10 10 10
Subtask #3 ∑ n , ∑ m ≤ 5000 \sum n,\sum m\le 5000 n,m5000 10 10 10
Subtask #4 v i = u i + 1 v_i=u_i+1 vi=ui+1 10 10 10
Subtask #5 u i = 1 u_i=1 ui=1 10 10 10
Subtask #6 u i ≠ 1 , v i ≠ 1 u_i\neq 1,v_i\neq 1 ui=1,vi=1 10 10 10
Subtask #7 T = 1 T=1 T=1 10 10 10
Subtask #8 30 30 30

思路 参考

  • 本道题通过画图,我们可以知道这道题是道联通性的问题,我们就可以用并查集,但怎么用呢,我们可以看下图:
    在这里插入图片描述
    对于图中我的疑问:

  • 问题1:为什么距离要为 2?因为题目要求:节点 u i u_i ui v i v_i vi 在这棵深蓝之树上唯一的不重复经过某个点的路径恰好有两条边,这个要求我们通过样例可以知道,就是使得每个连通块之间的距离为 2。

  • 问题2:当只有 1 个点的时候,是否正确?正确的,因为一个点也是一颗树。
    (有新的问题欢迎补充)

AC 代码

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N = 3e5+10;

int p[N];
int T;
int n,m;
int ans[N],cnt;

int find(int x){
    if(x!=p[x])p[x]=find(p[x]);
    return p[x];
}

int main(){
    scanf("%d",&T);
    
    while(T--){
        scanf("%d%d",&n,&m);
        
        cnt=0;
        
        for(int i=1;i<=n;i++)p[i]=i;
        
        for(int i=1;i<=m;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            p[find(a)]=find(b);
        }
        
        bool f=false;
        
        for(int i=1;i<=n;i++){
            if(find(i)==i){
                ans[++cnt]=i;
            }
            if(cnt==2){
                break;
            }
        }
        
        if(cnt<2)f=true;
        if(n==1){
            puts("Yes");
            continue;
        }
        
        if(f){
            puts("No");
        }else{
            puts("Yes");
            printf("%d %d\n",ans[1],ans[2]);//两个连通块连一条边即可
            for(int i=1;i<=n;i++){
                if(i!=ans[1]&&i!=ans[2]){
                    if(find(i)!=ans[1]){
                        printf("%d %d\n",i,ans[1]);
                    }else{
                        printf("%d %d\n",i,ans[2]);
                    }
                }
            }
        }
    }
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

green qwq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值