Description
给出一张n个点m条边的无向图,每条边有存在时间区间[li,ri],一开始一只Akagi在1号点,每个时刻她都必须要从某个点走到另一个点,每一条边所花费的时间为1,求Akagi走到点n的最小时间。
n,m<=5*1e5
Solution
听说篡改题面可以出赤城
这道题看上去没有什么下手的地方,我们挖掘一下性质。
考虑暴力,vis[i][j]表示能否在j时刻到达点i。
可以发现一条边(u,v)可以更新vis[v][t+1],vis[v][t+3],vis[v][t+5]…..vis[u][t+2],vis[u][t+4],vis[u][t+6]….
也就是说能够更新的时间的奇偶性是相同的。
我们不妨把每个点拆成两个点,一个奇点,一个偶点,然后一条边拆成四条单向边。
把这O(m)条边扔进一个堆里,按照最早的出现时间l从小到大排序。
然后对于一个点x,维护L[x]表示x最晚能够在哪个时刻达到。
这样可以很轻易地判断一条边是否能够更新L数组。
如果不能更新就把这条边挂在起点,等到能够更新的时候再拿出来。
可以发现这样子一条边只会访问两次,所以总复杂度为O(m log m)
Code
#include <set>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define rep(i,a,opt) for(int i=lst[a][opt];i;i=nxt[i])
using namespace std;
int read() {
char ch;
for(ch=getchar();ch<'0'||ch>'9';ch=getchar());
int x=ch-'0';
for(ch=getchar();ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x;
}
const int N=5*1e5+5;
int to[N<<2],nxt[N<<2],R[N<<2],lst[N][2],cnt;
void Add(int x,int y,int opt,int r) {
to[++cnt]=y;R[cnt]=r;nxt[cnt]=lst[x][opt];lst[x][opt]=cnt;
}
int n,m,x,y,l,r,L[N][2];
struct node{
int x,y,l,r;
friend bool operator < (node x,node y) {return x.l<y.l;}
};
multiset<node> s;
void Extend(int x,int l,int r,int opt) {
L[x][opt]=max(L[x][opt],r);
rep(i,x,opt) {
node p;
p.x=x;p.y=to[i];
p.l=l;p.r=R[i];
s.insert(p);
}
lst[x][opt]=0;
}
int main() {
n=read();m=read();
if (n==1) {puts("0");return 0;}
fo(i,1,m) {
x=read();y=read();l=read();r=read()-1;
node p;
p.x=x;p.y=y;p.l=l;p.r=r-(r-l)%2;s.insert(p);
p.x=y;p.y=x;p.l=l;p.r=r-(r-l)%2;s.insert(p);
p.x=x;p.y=y;p.l=l+1;p.r=r-!((r-l)%2);s.insert(p);
p.x=y;p.y=x;p.l=l+1;p.r=r-!((r-l)%2);s.insert(p);
}
memset(L,255,sizeof(L));
L[1][0]=0;
while (!s.empty()) {
node p=*s.begin();s.erase(s.begin());
if (p.l>p.r) continue;
int x=p.x,y=p.y,opt=p.l&1;
if (L[x][opt]>=p.l) {
if (y==n) {
printf("%d\n",p.l+1);
return 0;
}
Extend(y,p.l+1,p.r+1,opt^1);
} else Add(x,y,opt,p.r);
}
puts("-1");
return 0;
}