zoukankan      html  css  js  c++  java
  • 【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('
    ');}
    		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;
    }
    
  • 相关阅读:
    NSLog的各种打印格式符 和 打印CGRect时用NSStringFromCGRect
    mac os 隐藏文件夹的小技巧
    Triangle---minimum path sum
    Partition List
    Longest Common Prefix
    Count and Say
    C++ 左值 右值
    py2exe生成.exe(python3.4 尝试)
    longest incresing sequence
    Palindrome number
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ4239.html
Copyright © 2011-2022 走看看