把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【BZOJ4239】巴士走读(最短路)

点此看题面

大致题意:\(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('\n');}
		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;
}
posted @ 2020-06-02 07:59  TheLostWeak  阅读(178)  评论(0编辑  收藏  举报