原题链接:http://codeforces.com/problemset/problem/827/F
Description
给出一张n个点,m条边的无向图,经过每条边所花费的时间均为1
每一条边有一个出现时间区间
[
l
,
r
]
[l,r]
[l,r],也就是说,你只能在
[
l
,
r
−
1
]
[l,r-1]
[l,r−1]这一个时间区间内进入这条边,并且进入就不能回头,只能走到另一个端点。
你在时刻0时在1号点,每一个时刻必须走一条边,不能自己走进死胡同
求抵达n号点最少花费的时间。
n , m ≤ 500000 , l , r ≤ 1 0 9 n,m\leq 500000,l,r\leq 10^9 n,m≤500000,l,r≤109
Solution
直接做是很困难的,我们考虑发掘一下性质。
假设时刻0在1号点,我们有一条一直可用的边链接1,2
那么我们肯定能在时刻1,3,5,7,9,…,2k+1的时间走到2
如果我们拆点,将所有点分为偶点和奇点,原来的边拆成偶点与奇点之间的连边,相应修改一下出现区间。
此时一条边所能贡献的就是一段区间了。
我们将所有边按照出现时间排序,用一个堆来维护其中的最小值。
记录
l
a
s
t
[
i
]
last[i]
last[i]表示节点i目前所能停留的最晚时间,此外对于每个点都维护一个边的备用队列。
每次取出堆中的出现时间最短的边,记为
(
x
,
y
)
(x,y)
(x,y),如果last[x]大于等于出现时间,那就意味着这条边是可以走的。(因为我们按照奇偶拆点,上一次走过来这一个点的边出现时间一定小于当前点时间)
此时可以更新x,y的last,并可以更新y的最早抵达时间(更新的值就是这条边出现时间+1)
如果这条边不能走,那么塞入这条边的起点的队列备用。
如果某次更新last的时候使得这条边可以使用了,那么修改它的出现时间,并重新塞入堆中,并将其出队。
这样每条边最多只会被计算两次,复杂度是 O ( m log m ) O(m\log m) O(mlogm)的
Code
#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 500005
using namespace std;
int od[N],ev[N],n1,n,m,m1,a[4*N][4],last[2*N],ans,pt[2*N],sz[2*N];
struct node
{
int w;
friend bool operator <(node x,node y)
{
return a[x.w][2]>a[y.w][2];
}
};
priority_queue<node> h;
vector<int> d[2*N];
int lev(int k) {return(k&1)?k+1:k;}
int lod(int k) {return(k&1)?k:k+1;}
int sev(int k) {return(k&1)?k-1:k;}
int sod(int k) {return(k&1)?k:k-1;}
void read(int &x)
{
x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
}
void link(int x,int y,int l,int r)
{
if(l>r) return;
a[++m1][0]=x,a[m1][1]=y,a[m1][2]=l,a[m1][3]=r;
h.push((node){m1});
}
void update(int k,int st)
{
while(pt[k]<sz[k]&&a[d[k][pt[k]]][2]<=last[k])
{
int q=d[k][pt[k]];pt[k]++;
a[q][2]=max(a[q][2],st);
if(a[q][2]<=a[q][3]) h.push((node){q});
}
}
int main()
{
cin>>n>>m;
if(n==1) {printf("0\n");return 0;}
fo(i,1,n) od[i]=++n1,ev[i]=++n1;
fo(i,1,m)
{
int x,y,l,r;
read(x),read(y),read(l),read(r);
r--;
link(od[x],ev[y],lod(l),sod(r));
link(ev[x],od[y],lev(l),sev(r));
link(od[y],ev[x],lod(l),sod(r));
link(ev[y],od[x],lev(l),sev(r));
}
memset(last,128,sizeof(last));
last[ev[1]]=0;
while(!h.empty())
{
int w=h.top().w;h.pop();
if(last[a[w][0]]>=a[w][2])
{
if(a[w][1]==od[n]||a[w][1]==ev[n])
{
ans=a[w][2]+1;
printf("%d\n",ans);
return 0;
}
last[a[w][0]]=max(last[a[w][0]],a[w][3]);
last[a[w][1]]=max(last[a[w][1]],a[w][3]+1);
update(a[w][1],a[w][2]+1);
}
else d[a[w][0]].push_back(w),sz[a[w][0]]++;
}
printf("-1\n");
}