链接
分析
变量
//min_cnt, max_cnt分别是答案
int n, m, k, cnt, min_cnt, max_cnt;
//path是预先给的路径
int path[N], head[N], dis[N], vis[N];
导航要走的是最短路,我们假设path本身就是一个最短路,那么path上的u -> v这条边,一定满足u到终点的最短路比v到终点的最短路长1,但这样的话我们要对每一个点跑最短路,这是没有必要的,直接建反图就可以达到相同效果。dis是用反图跑dijkstra。我们通过dis确定了某条边是否在到终点的最短路上,于是可一下两种情况讨论
- 不在最短路上,此时必须重新导航
- 在最短路上,可以不用导航
对于1,我们必须要更换路线,所以++min_cnt, ++max_cnt
对于2, 可以不用更改路线,但是也可以更改成v以外别的最短路(最短路可能有多条),这时候只需要更新max_cnt。注意此时导航只能选择一条路,所以max_cnt最多只能加一次。
题目很长,理解起来不太方便,不过代码相对简单。
code
#include <cstdio>
#include <cctype>
#include <algorithm>
#include <queue>
#include <cstring>
typedef std::pair<int, int> P;
inline int read(){
int x = 0; char ch = getchar();
while (!isdigit(ch)){ch = getchar();}
while (isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48); ch = getchar();}
return x;
}
const int N = 2e5 + 10;
int n, m, k, cnt, min_cnt, max_cnt;
int path[N], head[N], dis[N], vis[N];
struct edge{
int to, nxt, reversed;
}e[N << 1];
inline void add(int u, int v, int rev){
e[++cnt] = {v, head[u], rev};
head[u] = cnt;
}
std::priority_queue<P, std::vector<P>, std::greater<> > que;
void dijkstra(int s){
memset(dis, 0x3f, sizeof dis);
dis[s] = 0;
que.push(std::make_pair(0, s));
while (!que.empty()){
int u = que.top().second;
que.pop();
if (vis[u]) continue;
vis[u] = 1;
for(int i = head[u]; ~i; i = e[i].nxt){
if (!e[i].reversed) continue;
int v = e[i].to;
if (dis[v] > dis[u] + 1){
dis[v] = dis[u] + 1;
que.push(std::make_pair(dis[v], v));
}
}
}
}
int main() {
memset(head, -1, sizeof head);
n = read(), m = read();
for (int i = 0, u, v; i < m; ++i) {
u = read(), v = read();
add(u, v, 0);
add(v, u, 1);
}
k = read();
for (int i = 0; i < k; ++i) path[i] = read();
dijkstra(path[k - 1]);
for (int i = 0; i < k - 1; ++i) {
int u = path[i], v = path[i + 1];
if (dis[v] != dis[u] - 1){
++min_cnt, ++max_cnt;
}
else{
for (int j = head[u]; ~j; j = e[j].nxt) {
if (e[j].reversed) continue;
int vv = e[j].to;
if (vv != v && dis[vv] == dis[v]){
++max_cnt;
break;
}
}
}
}
printf("%d %d\n", min_cnt, max_cnt);
return 0;
}