1. 程式人生 > 其它 >Two Spanning Trees(DFS樹,BFS樹)

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