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++ 栈和队列
    epoll 实际使用
    js数组的操作
    nodejs 基本类型和语法
    epoll 简单介绍及例子
    epoll 应用
    linux 查看系统状态方法
    struct stat结构体的详解和用法
    Asp.Net_获取IP地址
    Asp.Net_的传值跟存储值操作
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ2725.html
Copyright © 2011-2022 走看看