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审核
如果对你有帮助的话,请点个赞!

浙公网安备 33010602011771号