给一个无向图,其中的无向边有一些出现时间,人一开始在1号点,每一时刻他都需要不断移动,通过一条无向边的时间是1,问最早能在什么时刻到达n号点
(其实并不知道题解在说什么,膜完代码yy了一个感觉差不多的)
最朴素的暴力即记录v[i][j]表示在i这个时间点能否到达j,然后枚举每条边转移,复杂度是O(Tm)的
我们考虑这样做慢在哪里:每条边的生效时间让他变成了T条边,于是边的总数是Tm的
注意到,一条边(u,v)的贡献是一个时间段,且如果到达u的时间是t,那么他会使v[t+1][v],v[t+3][v]..v[t+1+2k][v],v[t+2][u],v[t+4][u]..变成true,
改成true的这些时间点对于一个点来说奇偶性是相同的,又是时间连续的一段,
所以我们可以把奇偶性相同的一些时间点一起更新,将点分裂成奇数时刻和偶数时刻的点,把边拆成4条有向边:在偶数u->奇数v,奇数u->偶数v,v->u同理
更新的时候一次性把这条边能更新到的全部时间点都更新,就能使得边的数目变回到O(m)条
此时显然不能枚举时间去计算边的贡献,我们按照边产生贡献的时间顺序去计算贡献
为了更新每条边能贡献到的所有时刻,对于每条边需要求出一个dp[i]表示第一次到达这条边的时刻
对于一个点的所有能产生贡献的出边,他们产生贡献的顺序一定是按照出现时间升序的,对于每个点的出边按出现时刻升序排序(vector竟然兹瓷排序),弄个当前弧,对每个点维护他的出现时间段li[x]~ri[x](注意他的时间段不一定是连续的,可能中间会断开,只需要维护最后的一段)
具体操作的话,一开始从1的偶数点的第一条出边开始,每次判断当前这条边是否能产生贡献,尝试用它的dpi去更新这条边的结束点的时间段,如果成功更新且结束点不在队列里,将结束点和他的当前弧放进队列,权为(u,v)出现后结束点当前弧出现的最早时间,每次当前出发点的当前弧成功产生贡献或已经消失后,更新他的当前弧,将点和他新的当前弧放进队列,权也为这条边能够出现的最早时间
code:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
using namespace std;
void read(int &x)
{
char c; while(!((c=getchar())>='0'&&c<='9'));
x=c-'0';
while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
}
inline int num(const int x,const int p){return (x-1)*2+p;}
inline void up(int &x,const int &y){if(xconst int maxn = 1010000;
const int maxm = 1010000;
int n,m;
struct edge
{
int y,l,r,rx;
edge(){}
edge(const int _y,const int _l,const int _r,const int _rx){y=_y;l=_l;r=_r;rx=_rx;}
};
vector v[maxn];
inline bool cmp(const edge x,const edge y){return x.lstruct node
{
int x,t;
node(){}
node(const int _x,const int _t){x=_x;t=_t;}
};
inline bool operator <(const node &x,const node &y){return x.t>y.t;}
priority_queueQ;
int li[maxn],ri[maxn],fir[maxn];
bool use[maxn];
int solve()
{
for(int i=0;i<2*n;i++) li[i]=ri[i]=-1,fir[i]=0;
li[0]=ri[0]=0;
if(v[0].size()>0) Q.push(node(0,v[0][0].l));
while(!Q.empty())
{
const node nx=Q.top(); Q.pop();
int x=nx.x,p=x&1;
use[x]=false;
int &i=fir[x];
const edge ne=v[x][i];
if(ri[x]continue;
if(li[x]<=ne.rx)
{
const int y=ne.y;
int rx=(ne.r&1)==p?ne.r-1:ne.r;
if(ri[y]1)
{
li[y]=max(li[x]+1,ne.l+1),ri[y]=rx;
if(!use[y]&&fir[y]!=v[y].size()) use[y]=true,Q.push(node(y,max(li[y],v[y][fir[y]].l)));
}
else if(ri[y]if(!use[y]&&fir[y]!=v[y].size()) use[y]=true,Q.push(node(y,max(li[y],v[y][fir[y]].l)));
}
}
i++;
if(itrue,Q.push(node(x,max(li[x],v[x][i].l)));
if(li[2*n-2]!=-1||li[2*n-1]!=-1)
{
int xx=li[2*n-2],yy=li[2*n-1];
if(xx==-1||yy==-1) return max(xx,yy);
return min(xx,yy);
}
}
return -1;
}
int main()
{
read(n); read(m);
if(n==1) { puts("0"); return 0; }
for(int i=1;i<=m;i++)
{
int x,y,l,r; read(x); read(y); read(l); read(r);
int lx=(l&1)?l+1:l,rx=(r&1)?r-1:r-2;
if(lx<=rx)
v[num(x,0)].push_back(edge(num(y,1),lx,r,rx)),
v[num(y,0)].push_back(edge(num(x,1),lx,r,rx));
lx=(l&1)?l:l+1,rx=(r&1)?r-2:r-1;
if(lx<=rx)
v[num(x,1)].push_back(edge(num(y,0),lx,r,rx)),
v[num(y,1)].push_back(edge(num(x,0),lx,r,rx));
}
for(int i=0;i<2*n;i++) sort(v[i].begin(),v[i].end(),cmp);
printf("%d\n",solve());
return 0;
}