zoukankan      html  css  js  c++  java
  • 【BZOJ2725】[Violet 6] 故乡的梦(最短路+线段树)

    点此看题面

    大致题意: 给定一张图,每次询问禁止走某条路后(S)(T)间的最短路。

    前言

    以前的我只是菜,现在的我不仅菜,还瞎。。。

    我居然以为起点和终点是每次询问给出的,结果想了半天。。。

    最短路

    考虑我们找出原图任意一条(S)(T)的最短路。

    显然,在禁止走不在最短路上的边时,最短路不会受到影响。

    否则,假设我们断开了((x,y))(设(x)(S)较近,(y)(T)较近),那么我们必然是找到分别在最短路中(S)(x)(y)(T)的路径上的两个点(u,v),使得最短路中(S)(u)的距离加上(u)(v)的距离加上最短路中(v)(T)的距离最小。

    我们可以给(S)(T)最短路上的点依次重新标号,其中第(i)个点(x)(rk_x=i)

    然后令(fS_x,fT_x)分别表示(x)(S,T)最短路上第一个位于(S)(T)最短路上点的(rk)(可以拓扑求,详见代码)。

    于是对于一条不在最短路上的边((u,v)),强制经过它的最短路就是(disS_u+val_{u,v}+disT_v),它能替代的最短路上边的区间就是([fS_u,fT_v-1])

    这个操作就相当于是相当于区间修改,单点查询最小值。

    则我们显然可以用线段树来维护。

    代码

    #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 200000
    #define LL long long
    #define INF 1e18
    #define Gmin(x,y) (x>(y)&&(x=(y)))
    #define min(x,y) ((x)<(y)?(x):(y))
    #define add(x,y,v) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].val=v)
    using namespace std;
    int n,m,s,t,ee,cnt,lnk[N+5],lst[N+5],rk[N+5];struct line {int x,y,v;}p[N+5];
    struct edge {int to,nxt,val;}e[2*N+5];
    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 writeln(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);pc('
    ');}
    		I void NA() {pc('I'),pc('n'),pc('f'),pc('i'),pc('n'),pc('i'),pc('t'),pc('y'),pc('
    ');}
    		I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
    		#undef D
    }F;
    class Dijkstra//最短路
    {
    	private:
    		#define mp make_pair
    		#define fir first
    		#define sec second
    		LL dis[N+5];int vis[N+5],pre[N+5],f[N+5],d[N+5],p[N+5];
    		typedef pair<LL,int> Pr;priority_queue<Pr,vector<Pr>,greater<Pr> > q;
    	public:
    		I LL operator [] (CI x) {return dis[x];}
    		I int operator () (CI x) {return f[x];} 
    		I void Dij(CI x)//最短路
    		{
    			RI i;Pr k;for(i=1;i<=n;++i) dis[i]=INF;q.push(mp(dis[x]=0,x));
    			W(!q.empty())
    			{
    				if(k=q.top(),q.pop(),vis[k.sec]) continue;vis[k.sec]=1;
    				for(i=lnk[k.sec];i;i=e[i].nxt) k.fir+e[i].val<dis[e[i].to]&&
    					(q.push(mp(dis[e[i].to]=k.fir+e[i].val,e[i].to)),pre[e[i].to]=k.sec);
    			}
    		}
    		I void Topo()//拓扑求出f
    		{
    			RI i,k,H=1,T=0;for(i=1;i<=n;++i) ++d[pre[i]];for(i=1;i<=n;++i) !d[i]&&(p[++T]=i);
    			W(H<=T) !--d[pre[k=p[H++]]]&&(p[++T]=pre[k]);
    			for(i=T;i;--i) f[p[i]]=rk[p[i]]?rk[p[i]]:f[pre[p[i]]];//倒序枚举数组中的点求f
    		}
    		I void GetRk(CI s,RI t)//重新标号
    		{
    			for(RI i=1;i<=n;++i) lst[i]=pre[i];
    			W(rk[t]=++cnt,t^s) t=lst[t];for(RI i=1;i<=n;++i) rk[i]&&(rk[i]=cnt-rk[i]+1);//枚举每个点
    		}
    }D1,D2;
    class SegmentTree//线段树,这里用了标记永久化
    {
    	private:
    		#define PT CI l=1,CI r=cnt,CI rt=1
    		#define LT l,mid,rt<<1
    		#define RT mid+1,r,rt<<1|1
    		LL V[N<<2];
    	public:
    		I void Build(PT) {if(V[rt]=INF,l==r) return;int mid=l+r>>1;Build(LT),Build(RT);}
    		I void U(CI L,CI R,LL v,PT)//区间修改
    		{
    			if(!L||!R||L>R) return;if(L<=l&&r<=R) return (void)Gmin(V[rt],v);
    			int mid=l+r>>1;L<=mid&&(U(L,R,v,LT),0),R>mid&&(U(L,R,v,RT),0);
    		}
    		I LL Q(CI x,PT)//单点查询
    		{
    			if(l==r) return V[rt];int mid=l+r>>1;
    			LL t=x<=mid?Q(x,LT):Q(x,RT);return min(t,V[rt]);
    		}
    }S;
    int main()
    
    	RI Qt,i,x,y,z;for(F.read(n,m),i=1;i<=m;++i)
    		F.read(p[i].x,p[i].y,p[i].v),add(p[i].x,p[i].y,p[i].v),add(p[i].y,p[i].x,p[i].v);
    	if(F.read(s,t,Qt),s==t) {for(i=1;i<=Qt;++i) puts("0");return 0;}//特判
    	if(D1.Dij(s),D1[t]==INF) {for(i=1;i<=Qt;++i) puts("Infinity");return 0;}//特判
    	D1.GetRk(s,t),D1.Topo(),D2.Dij(t),D2.Topo(),S.Build();
    	#define Check(x,y) (!rk[x]||!rk[y]||(rk[x]^(rk[y]+1)&&rk[y]^(rk[x]+1)))//判断一条边是否在最短路上
    	for(i=1;i<=m;++i) Check(p[i].x,p[i].y)&&//枚举不在最短路上的边
    	(
    		S.U(D1(p[i].x),D2(p[i].y)-1,D1[p[i].x]+p[i].v+D2[p[i].y]),//(x,y)
    		S.U(D1(p[i].y),D2(p[i].x)-1,D1[p[i].y]+p[i].v+D2[p[i].x]),0//(y,x)
    	);
    	LL k;W(Qt--)//处理询问
    	{
    		if(F.read(x,y),Check(x,y)) {F.writeln(D1[t]);continue;}//不在最短路上直接输出最短路
    		(k=S.Q(min(rk[x],rk[y])))==INF?F.NA():F.writeln(k);//不连通输出无解,否则输出答案
    	}return F.clear(),0;
    }
    
  • 相关阅读:
    C语言-if语句
    C语言-表达式
    C语言-基础
    Java for LeetCode 187 Repeated DNA Sequences
    Java for LeetCode 179 Largest Number
    Java for LeetCode 174 Dungeon Game
    Java for LeetCode 173 Binary Search Tree Iterator
    Java for LeetCode 172 Factorial Trailing Zeroes
    Java for LeetCode 171 Excel Sheet Column Number
    Java for LeetCode 169 Majority Element
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ2725.html
Copyright © 2011-2022 走看看