• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
PCMSFV
博客园    首页    新随笔    联系   管理    订阅  订阅

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 on 2026-03-31 22:16  PCMSFV  阅读(20)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3