水影若深蓝
题目背景
帆帆爱明日方舟,更爱水月和深蓝之树。但在龙年,他想构建一棵独属于他的深蓝之树。
题目描述
在梦里,在朦胧的水影中,帆帆看见了这棵只属于他的深蓝之树。
当一切梦沉寂,他却遗忘了所有,只记得树上有 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 n−1 行输出 n − 1 n-1 n−1 对正整数 ( u , v ) (u,v) (u,v),表示你给出的树上的 n − 1 n-1 n−1 条边。
本题采用 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 1≤T≤105, n ≥ 1 n\ge 1 n≥1, m ≥ 0 m\ge 0 m≥0, 1 ≤ ∑ n ≤ 3 × 1 0 5 1\le \sum n\le 3\times 10^5 1≤∑n≤3×105, 0 ≤ ∑ m ≤ 3 × 1 0 5 0\le \sum m\le 3\times 10^5 0≤∑m≤3×105, 1 ≤ u i , v i ≤ n 1\le u_i,v_i\le n 1≤ui,vi≤n, u i ≠ v i u_i\neq v_i ui=vi。
本题采用捆绑测试。
子任务编号 | 特殊限制 | 分值 |
---|---|---|
Subtask #1 | ∑ n , ∑ m ≤ 9 \sum n,\sum m\le 9 ∑n,∑m≤9 | 10 10 10 |
Subtask #2 | ∑ n , ∑ m ≤ 50 \sum n,\sum m\le 50 ∑n,∑m≤50 | 10 10 10 |
Subtask #3 | ∑ n , ∑ m ≤ 5000 \sum n,\sum m\le 5000 ∑n,∑m≤5000 | 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;
}