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\)

解析

不难想到暴力做法,dfs/bfs遍历每一个点,得到对于它的最大值
复杂度\(O(n^2)\),这是代码\(90pts\)
可以想到用时间戳解决多次\(memset()\)的问题,但是这样也依旧\(90pts\)

我们知道这是有向图
这里就需要一个反向建图的做法

反向建图,把所有边的朝向反过来,例如a -> b, a <- b

有什么用呢?
不妨按照编号从大到小遍历节点,则每一次能遍历到的,之前没有被访问过的节点,就一定和当前的出发节点相等,因为后面即使遍历到了,也不如这个大
所以对于遍历过的节点,直接在它这里\(return\),因为后面的节点一定也被访问过,这样每个节点最多被访问一次,复杂度\(O(n)\)

AC代码

#include <bits/stdc++.h>

using namespace std;

constexpr int N = 1e5 + 2;

vector<int> a[N];

int vis[N];
int b = 0;

inline void dfs(int now) {
    if (vis[now]) return;
    vis[now] = b;
    for (int i : a[now]) {
        if (vis[i]) continue;
        dfs(i);
    }
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);

    int n, m;
    cin >> n >> m;

    for (int i = 1; i <= m; i++) {
        int u, v;
        cin >> u >> v;
        a[v].push_back(u);
    }

    for (b = n; b >= 1; b--) {
        dfs(b);
    }

    for (int i = 1; i <= n; i++) {
        cout << vis[i] << " ";
    }
}

该题解由deepseek审核
如果对你有帮助的话,请点个赞!

posted @ 2026-03-31 22:16  PCMSFV  阅读(12)  评论(0)    收藏  举报