【Codeforces 827F Dirty Arkady‘s Kitchen】【图论】

题意

给一个 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,m5105

分析

注意到可以在一条边上反复横跳,不妨把点按照奇偶性拆分成两个,边按照两个方向和奇偶性拆分成四条。
维护 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;
}

你可能感兴趣的:(图论)