Two Spanning Trees(DFS树,BFS树)
题意
给定一个\(n\)个点,\(m\)条边的无向连通图\(G\),并且\(G\)是简单图。找到两个满足如下条件的生成树\(T_1\)和\(T_2\):
-
如果我们把\(T_1\)看作以\(1\)为根节点的有向图,对于任意一条不包含在\(T_1\)中的边(非树边),两个点中一个点是另一个点在\(T_1\)中的祖先。
-
如果我们把\(T_2\)看作以\(1\)为根节点的有向图,对于任意一条不包含在\(T_2\)中的边(非树边),不能出现两个点中一个是另一个在\(T_2\)中的祖先的情况。
数据范围
\(2 \leq n \leq 2 \times 10^5\)
\(1 \leq m \leq 2 \times 10^5\)
思路
这道题是DFS树和BFS树的模板题。
首先介绍DFS树。对一个无向连通图\(G\)进行深度优先遍历,得到的树为DFS树。其中,遍历到的边为树边,回溯的边为回边(非树边)。DFS树的一个重要性质是,图的回边连接的都是一个顶点和它在DFS树中的子孙节点。根据这条性质,我们发现DFS树满足\(T_1\)的要求。
然后介绍BFS树。对一个无向连通图\(G\)进行宽度优先遍历,得到的树为BFS树。其中,遍历到的边为树边,回溯的边为回边(非树边)。BFS树的一个重要性质是,图的回边连接的两点一定在BFS树的同一层或者相邻的两层。并且,如果处于相邻两层,其中一个点不可能是另一个点的父节点(因为如果是父节点,那么就是树边了)。根据这条性质,我们发现BFS树满足\(T_2\)的要求。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 200010, M = 2 * N;
int n, m;
int h[N], e[M], ne[M], idx;
bool st[N];
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
void dfs(int u)
{
st[u] = true;
for(int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if(!st[j]) {
printf("%d %d\n", u, j);
st[j] = true;
dfs(j);
}
}
}
void bfs(int u)
{
queue<int> que;
que.push(u);
st[u] = true;
while(que.size()) {
int t = que.front();
que.pop();
for(int i = h[t]; ~i; i = ne[i]) {
int j = e[i];
if(!st[j]) {
st[j] = true;
que.push(j);
printf("%d %d\n", t, j);
}
}
}
}
int main()
{
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
for(int i = 0; i < m; i ++) {
int a, b;
scanf("%d%d", &a, &b);
add(a, b), add(b, a);
}
dfs(1);
memset(st, 0, sizeof st);
bfs(1);
return 0;
}