大致题意: 有(n)个点,且有(m)辆巴士,每辆巴士在第(X_i)时刻从(A_i)出发,在第(Y_i)时刻到达(B_i)。多次询问在规定到达(n)号点的最晚时间时,从(1)号点出发的最晚时间。
前言
自己瞎想的做法,感觉非常暴力,但复杂度应该没问题。
关键是很好写,也没怎么调试就过了(只是一开始智障写(dijkstra)习惯性开了小根堆。。。)。
考虑一个暴力
假如我们按(Y_i)从小到大的顺序枚举每一条连向(n)号点的边,然后从这条边出发在反向图上跑最短路。
对于每个点记下到达它的最晚时间(dis_x),一条反向边能走需要满足(dis_{B_i}ge Y_i),满足条件即可贪心地用(X_i)去更新(dis_{A_i})。
最终(dis_1)就可以对所有最晚时间大于等于枚举边(Y_i)的询问产生贡献。
虽说这个做法显然会(T)掉,但其正确性也是显然的,因此只要考虑如何优化即可。
优化
我们可以发现,显然枚举边的(Y_i)越小,对答案的贡献范围越大。
因此,如果一条边已经被之前的枚举边访问过,就无需再访问了。
为什么可以这么做呢?
因为这题最短路很特殊,无论你的(dis)是多少,只要访问了这条边,都会一样地变成(X_i),也就是说你用(Y_i)更大的枚举边去访问这条边,只会得到一样的结果,而贡献范围显然不如(Y_i)较小的枚举边。
于是,尽管我们要跑很多次(Dijkstra),但每条边只会经过一次,复杂度也就得到了保证。
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define M 300000
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
using namespace std;
int n,m;struct edge {int to,X,Y;};vector<edge> E[N+5];
typedef pair<int,int> Pr;vector<Pr> ans;
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define pc(c) (C==E&&(clear(),0),*C++=c)
#define D isdigit(c=tc())
int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
public:
I FastIO() {A=B=FI,C=FO,E=FO+FS;}
Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
Tp I void write(Ty x) {x<0&&(pc('-'),x=-x);W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);}
Tp I void writeln(Con Ty& x) {write(x),pc('
');}
I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
#undef D
}F;
class Dijkstra
{
private:
int dis[N+5],vis[N+5],cur[N+5];priority_queue<Pr> q;
public:
I void Init() {memset(dis,-1,sizeof(dis)),ans.pb(mp(0,-1));}//初始化为-1,并在答案中加入-1表示无解
I void Dij(Con edge& e)
{
static int ti=0;vis[n]=++ti;RI i,sz;Pr k;if(dis[e.to]>=e.X) return;
q.push(mp(dis[e.to]=e.X,e.to));W(!q.empty())
{
if(k=q.top(),q.pop(),vis[k.sec]==ti) continue;vis[k.sec]=ti;
#define Eg E[k.sec][i]
for(sz=E[k.sec].size(),i=cur[k.sec];i^sz&&k.fir>=Eg.Y;++i)//cur记录当前枚举到的边
cur[k.sec]=i,dis[Eg.to]<Eg.X&&(q.push(mp(dis[Eg.to]=Eg.X,Eg.to)),0);//Dij转移
}ans.pb(mp(e.Y,dis[1]));//将当前距离存入答案数组
}
}D;
I bool cmp(Con edge& x,Con edge& y) {return x.Y<y.Y;}
int main()
{
RI i,a,b,c,d;for(F.read(n,m),i=1;i<=m;++i) F.read(a,b,c,d),E[b].pb((edge){a,c,d});//存反向边
for(i=1;i<=n;++i) sort(E[i].begin(),E[i].end(),cmp);//按Y[i]排序
RI sz=E[n].size();for(D.Init(),i=0;i^sz;++i) D.Dij(E[n][i]);//枚举n号点的边跑Dijkstra
RI Qt;F.read(Qt);W(Qt--) F.read(a),//读入询问
F.writeln((--upper_bound(ans.begin(),ans.end(),mp(a,(int)1e9)))->sec);//在ans的vector中查找答案
return F.clear(),0;
}