给一个 n n n 个点 m m m 条边的无向图,经过每条边的时间为 1 1 1,每条边有一个可以使用的时间区间 [ l i , r i ] [l_i,r_i] [li,ri]。每个时刻必须移动到与当前点有边相连的一个点上。问从 1 1 1 到 n n n 花费的最小时间。
n , m ≤ 5 ∗ 1 0 5 n,m\le 5*10^5 n,m≤5∗105
注意到可以在一条边上反复横跳,不妨把点按照奇偶性拆分成两个,边按照两个方向和奇偶性拆分成四条。
维护 l a t e x , 0 / 1 late_{x,0/1} latex,0/1 表示在奇/偶时刻到达 x x x 的最晚时间。按 l i l_i li 从小到大加入边。若到达起点的最晚时间不小于 l i l_i li,表明这条边是可以使用的,并用这条边更新终点。否则把这条边挂在起点上,等下一次访问起点时再取出这条边。
时间复杂度 O ( m log m ) O(m\log m) O(mlogm)。
#include
using namespace std;
const int N = 500005;
int n, m, late[N][2];
struct edge
{
int u, v, l, r;
edge(int u, int v, int l, int r):u(u), v(v), l(l), r(r) {}
bool operator < (const edge & a) const {return l > a.l;}
};
priority_queue<edge> que;
vector<edge> vec[N][2];
void update(int x, int st, int ed)
{
late[x][st & 1] = max(late[x][st & 1], ed);
for (edge e : vec[x][st & 1])
{
edge t = e;
t.l = st;
que.push(t);
}
vec[x][st & 1].clear();
}
int main()
{
scanf("%d%d", &n, &m);
if (n == 1) {printf("%d\n", 0); return 0;}
for (int i = 1; i <= m; i++)
{
int a, b, l, r; scanf("%d%d%d%d", &a, &b, &l, &r); r--;
int w = (l ^ r) & 1;
que.push(edge(a, b, l, r - w));
que.push(edge(b, a, l, r - w));
que.push(edge(a, b, l + 1, r - (w ^ 1)));
que.push(edge(b, a, l + 1, r - (w ^ 1)));
}
for (int i = 1; i <= n; i++) late[i][0] = late[i][1] = -1;
late[1][0] = 0;
int ans = -1;
while (!que.empty())
{
edge e = que.top(); que.pop();
if (e.l > e.r) continue;
if (late[e.u][e.l & 1] >= e.l)
{
if (e.v == n) {ans = e.l + 1; break;}
update(e.v, e.l + 1, e.r + 1);
}
else vec[e.u][e.l & 1].push_back(e);
}
printf("%d\n", ans);
return 0;
}