P3916 图的遍历

P3916 图的遍历

题目描述

给出 $N$ 个点,$M$ 条边的有向图,对于每个点 $v$,令 $A(v)$ 表示从点 $v$ 出发,能到达的编号最大的点。现在请求出 $A(1),A(2),\dots,A(N)$ 的值。

输入格式

第 $1$ 行 $2$ 个整数 $N,M$,表示点数和边数。

接下来 $M$ 行,每行 $2$ 个整数 $U_i,V_i$,表示边 $(U_i,V_i)$。点用 $1,2,\dots,N$ 编号。

输出格式

一行 $N$ 个整数 $A(1),A(2),\dots,A(N)$。

输入输出样例 #1

输入 #1

4 3
1 2
2 4
4 3

输出 #1

4 4 3 4

说明/提示

  • 对于 $60%$ 的数据,$1 \leq N,M \leq 10^3$。
  • 对于 $100%$ 的数据,$1 \leq N,M \leq 10^5$。

思路分析 + 代码实现

用反向边建图,这样构建的反向图可以将原问题转化为从每个节点出发在反向图中能到达哪些节点。从编号最大的点开始遍历(这里写了 BFS 和 DFS 两种做法)。如果遍历到的点未被遍历过,则记录答案,继续遍历。时间复杂度和空间复杂度都是为 $O(N+M)$ ,代码如下:

#include <bits/stdc++.h>
using namespace std;
constexpr int MAXN = 1e5 + 5;
vector<int> G[MAXN];
int Dans[MAXN], Bans[MAXN];
void dfs(int x, int v) {
    if (Dans[x]) return;
    Dans[x] = v;
    for(int i : G[x])
        dfs(i, v);
}
void bfs(int x, int v) {
    if (Bans[x]) return;
    Bans[x] = v;
    queue<int> Q;
    Q.push(x);
    while (!Q.empty()) {
        int u = Q.front();
        Q.pop();
        for(int i : G[u]) {
            if (Bans[i]) continue;
            Bans[i] = v;
            Q.push(i);
        }
    }
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int N, M;
    cin >> N >> M;
    for(int u, v, i = 0; i < M; i++) {
        cin >> u >> v;
        G[v].push_back(u);
    }
    for(int i = N; i > 0; i--)
        dfs(i, i);
    for(int i = N; i > 0; i--)
        bfs(i, i);
    for(int i = 1; i <= N; i++)
        cout << Bans[i] << " ";
        // cout << Dans[i] << " ";
    return 0;
}
使用 Hugo 构建
主题 StackJimmy 设计