动态SPFA水题,和之前某道魔法森林相似
我们先存边,然后按L排个序,之后在将每一条边加入,每加入一条边,就以当前边的两个端点为起点,做一次双源最短路,不难发现,加入这条边后,这两个点最可能更新其他点
初始化时除了1之外,其他点的dis=0,dis[1]=inf,然后我们按照R来做最长路,考虑点u,v,边 e,dis[v]可能的取值有三种,v之前被更新过,所以有一个dis[v],考虑题目的限制,这次路线的的区间是最大的L~最小的R,因为我们按照L从小到达排序,所以图中最大的L就是刚刚加入的边,所以不需要考虑L,那么考虑R,在到达u之前,可能存在一条边p是到达u必须要走的边且是必须边中最小的(自己感性理解,必须边是一条链,那么必须要走这条最小的边,那么R只能是这条边的R),val[p]<val[e],我们之前保证dis[v]<dis[u],那么显然dis[v]=dis[u]因为e这条边比dis[u]大,所以这条边的R也是dis[u],然后另一种情况val[p]>val[e],那么在这条边之前没有比这条边R小的,但这条边又是u->v的必须边,所以dis[v]只能等于val[e],所以我们得到在跑SPFA时,更新条件为
if (dis[v]>min(dis[u],val[e])) dis[v]>min(dis[u],val[e]);
然后记录SPFA中max(dis[n]-SPFA前加入的边的L)
代码
//By AcerMo
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int M=5050;
struct edge
{
int x,y,w1,w2;
bool friend operator < (edge a,edge b)
{
return a.w1<b.w1;
}
}e[M*2];
int n,m;
int dis[M],vis[M];
int cnt,to[M*2],nxt[M*2],head[M*2],co1[M*2],co2[M*2];
queue<int>q;
inline int read()
{
int x=0;char ch=getchar();
while (ch<'0'||ch>'9') ch=getchar();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
inline void add(int x,int y,int w,int c)
{
to[++cnt]=y;nxt[cnt]=head[x];co1[cnt]=w;co2[cnt]=c;head[x]=cnt;
to[++cnt]=x;nxt[cnt]=head[y];co1[cnt]=w;co2[cnt]=c;head[y]=cnt;
return ;
}
inline void SPFA(int st,int ed)
{
q.push(st);q.push(ed);
while (q.size())
{
int u=q.front();q.pop();vis[u]=0;
for (int i=head[u];i;i=nxt[i])
{
if (dis[to[i]]<min(dis[u],co2[i]))
{
dis[to[i]]=min(dis[u],co2[i]);
if (!vis[to[i]])
vis[to[i]]=1,q.push(to[i]);
}
}
}
return ;
}
signed main()
{
n=read();m=read();int ans=0,l=0,r=0;
for (int i=1;i<=m;i++)
e[i].x=read(),e[i].y=read(),e[i].w1=read(),e[i].w2=read();
sort(e+1,e+m+1);dis[1]=2e9;q.push(1);
for (int i=1;i<=m;i++)
{
add(e[i].x,e[i].y,e[i].w1,e[i].w2);SPFA(e[i].x,e[i].y);
if (ans<dis[n]-e[i].w1) ans=dis[n]-e[i].w1,l=e[i].w1,r=dis[n];
}
cout<<ans+1<<endl;
for (int i=l;i<r;i++) cout<<i<<" ";cout<<r;
return 0;
}