[USACO23FEB] Moo Route II S 题解
思路
首先,这道题目是一个比较明显的图论最短路,但是由于时间可以从后往前,想直接跑最短路而不用玄学方法的话——迪杰斯特拉怕只有怔怔地望着的份了。当然,是否有玄学方法我就不得而知了。
SPFA 的 LLL 优化
“燕子去了,有再来的时候;杨柳枯了,有再青的时候;桃花谢了,有再开的时候。”聪明的,你告诉我,SPFA 死了就没有再活的时候吗?
非然也。
我们直接按照题目的边作为图上的边,跑 SPFA 算法。注意一开始我们对一个点连出去的所有边排序,那么这样对于 SPFA 中枚举边的时候发现边被分为两类,刚好是两段——一段是起始时间太早导致无法乘坐的,位于其后面的一段是能去拓展其它点的边。
那么我们每次维护分隔点的位置即可。
“我掩面叹息,但是新来的日子的影儿又开始在叹息里闪过了”
我掩面叹息,但是强大的 SPFA 早已在叹息里跑过了。
此做法的代码是通过了,但是我就不给了。
记忆化 SPFA
这只能应对有特殊性质的题目。
这个做法时间复杂度有保证,代码又异常短。
首先还是对边排序,还是跑 SPFA 最短路。改动的只是我们把每次拓展出去过的边都删掉。毕竟每条边只可能在第一次遍历到时更新其它点,从第二次开始必定不会更新成功。
#include <bits/stdc++.h>
#define L(i, a, b) for(register int i = a; i <= b; i++)
#define R(i, a, b) for(register int i = a; i >= b; i--)
using namespace std;
const int N = 2e5 + 10;
struct Edge{
int v, s, t;
friend bool operator < (Edge a, Edge b){
return a.s == b.s? a.v < b.v : a.s < b.s;
}
};
int n, m, a[N], dis[N], vis[N], id[N];
queue<int> q; vector<Edge> g[N];
void Spfa(){
L(i, 2, n) dis[i] = 2e9;
vis[1] = 1, q.push(1);
while(!q.empty()){
int u = q.front(); q.pop();
int tm = (u == 1? 0 : dis[u] + a[u]);
while(id[u] && g[u][id[u] - 1].s >= tm) id[u]--;
vis[u] = 0;
L(i, id[u], (int)g[u].size() - 1){
int v = g[u][i].v, t = g[u][i].t;
if(t < dis[v]){
dis[v] = t;
if(!vis[v]) vis[v] = 1, q.push(v);
}
}
while(g[u].size() > id[u]) g[u].pop_back(); id[u] = g[u].size();
}
}
int main(){
scanf("%d%d", &n, &m);
L(i, 1, m){
int u, v, s, t;
scanf("%d%d%d%d", &u, &s, &v, &t);
g[u].push_back({v, s, t});
}
L(i, 1, n){
scanf("%d", &a[i]);
sort(g[i].begin(), g[i].end()), id[i] = g[i].size();
}
Spfa();
L(i, 1, n){
if(dis[i] == 2e9) printf("-1\n");
else printf("%d\n", dis[i]);
}
return 0;
}