Description
给定一个 n n n 个点 m m m 条边的有向图,和一个长度为 k k k 的起点终点确定的路径 p p p。从 p 1 p_1 p1 到 p t p_t pt,如果路径上的一个点不在最短路上,那么导航次数多一次,导航不会影响路径。求最少和最多的导航次数。
2 ≤ n , m ≤ 2 × 1 0 5 2 \leq n,m \leq 2 \times 10^5 2≤n,m≤2×105。
Solution
先跑从 t t t 出发的 bfs,这个要建出反向边。如果从最短路上的一个点 x x x 出发,下一个走的点为 y y y 还在最短路上当且仅当 d i s y = d i s x − 1 dis_y = dis_x - 1 disy=disx−1。所以从 s s s 走,分类讨论
-
d i s p i = d i s p i − 1 − 1 dis_{p_i} = dis_{p_{i-1}} - 1 dispi=dispi−1−1。那么最少次数和上次相等。如果从 k i k_i ki 走还有满足最短路的其他点,那么最多可以多一次。
-
d i s p i ≠ d i s p i − 1 − 1 dis_{p_i} \not= dis_{p_{i-1}} - 1 dispi=dispi−1−1。一定要重新规划了,最小和最大都多一次。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5, M = 5e5 + 5, INF = 0x3f3f3f3f;
inline int read() {
int x = 0, f = 0; char ch = 0;
while (!isdigit(ch)) f |= ch == '-', ch = getchar();
while (isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
return f ? -x : x;
}
struct edge{
int to, nxt;
}e[M];
int head[N], tot;
void addedge(int x, int y) {
e[++tot].to = y, e[tot].nxt = head[x], head[x] = tot;
}
int n, m, k;
int p[N], dis[N];
void bfs(int s) {
queue <int> q;
dis[s] = 1; q.push(s);
while (!q.empty()) {
int x = q.front(); q.pop();
for (int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
if (!dis[y]) {
dis[y] = dis[x] + 1;
q.push(y);
}
}
}
}
int ex[N], ey[N];
int main() {
n = read(), m = read();
for (int i = 1; i <= m; i++) {
ex[i] = read(); ey[i] = read();
addedge(ey[i], ex[i]);
}
k = read();
for (int i = 1; i <= k; i++) p[i] = read();
bfs(p[k]);
int mn = 0, mx = 0;
memset(head, 0, sizeof(head)); tot = 0;
for (int i = 1; i <= m; i++) addedge(ex[i], ey[i]);
for (int i = 1; i < k; i++) {
if (dis[p[i]] != dis[p[i + 1]] + 1) mn++, mx++;
else {
for (int j = head[p[i]]; j; j = e[j].nxt) {
int y = e[j].to;
if (dis[y] == dis[p[i + 1]] && y != p[i + 1]) {
mx++; break;
}
}
}
}
printf("%d %d\n", mn, mx);
return 0;
}