【2019/07/20测试 T3】最短路

传送门


Problem

在一张 n n n 个点 m m m 条边的有向图上,AliceBob 将要从共同的起点出发,沿最短路分别走到目标点 u u u v v v,如果有多条最短路,则取字典序最小的路径。

这里的字典序指路径经过的所有点构成的字典序。定义一条路径 p = ⟨ u 1 , u 2 , ⋯ , u n ⟩ p=⟨u_1,u_2,⋯,u_n⟩ p=u1,u2,,un 的字典序小于另一条路径 q = ⟨ v 1 , v 2 , ⋯ , v m ⟩ q=⟨v_1,v_2,⋯,v_m⟩ q=v1,v2,,vm,当且仅当满足以下两条件之一:

  • ∃ k ( 1 ≤ k ≤ m i n ( n , m ) ) ∃k(1≤k≤min(n,m)) k(1kmin(n,m)),满足 u 1 = v 1 , u 2 = v 2 , ⋯ , u k − 1 = v k − 1 u_1=v_1,u_2=v_2,⋯,u_{k−1}=v_{k−1} u1=v1,u2=v2,,uk1=vk1,且 u k &lt; v k u_k&lt;v_k uk<vk
  • n &lt; m n&lt;m n<m,且 ∀ i ( 1 ≤ i ≤ n ) ∀i(1≤i≤n) i(1in),都有 u i = v i u_i=v_i ui=vi

其中 ∃ ∃ 符号意为 “ “ 存在 ” ” ∀ ∀ 符号意为 “ “ 对于任意 ” ”

q q q 次询问,每次询问目标点 u u u v v v ,找到起点(可以与 u u u v v v 重合)使两条最短路的公共部分长度最长,求此最长长度。

如果不存在符合某个起点能到达 u , v u,v u,v,输出 − 1 -1 1

对于 100 % 100\% 100% 的数据, 1 ≤ u , v ≤ n ≤ 2000 1≤u,v≤n≤2000 1u,vn2000 1 ≤ m , q ≤ 6000 1≤m,q≤6000 1m,q6000 0 ≤ ∣ w ∣ ≤ 1 0 4 0≤|w|≤10^4 0w104,保证不存在负环。


Solution

解这道题之前,先介绍一下最短路树,顾名思义,最短路树是图中的一个生成树,且各个点到根的距离就是单源最短路。

那假如我们建出了最短路树,那么两点间最短路的公共部分长度不就是它们的 l c a lca lca 到根节点的距离吗。

那这道题就好做了呀。

为了让字典序最小,我们可以用 v e c t o r \mathrm{vector} vector 存图,建树前 s o r t sort sort 一下保证编号小的点先访问即可。

于是得到一个算法:先把询问离线下来,枚举起点跑最短路建出当前的最短路树,再枚举询问更新答案。

但有个小瑕疵,即图中有负边权,不能用 d i j k s t r a dijkstra dijkstra,而用 n n n s p f a spfa spfa 又极容易被卡,怎么办呢?

有这样一个办法:建立虚点向每个点连边权为 0 0 0 的边,然后用 s p f a spfa spfa 计算出 0 0 0 到每个点的最短路 d i s 1 dis_1 dis1(只能是 0 0 0 或负数),然后将 ( u , v ) (u,v) (u,v) 这条边的边权 w w w 改为 w + d i s 1 u − d i s 1 v w+dis_{1_u}-dis_{1_v} w+dis1udis1v(改过后的 w w w 一定为正),就可以跑 d i j k s t r a dijkstra dijkstra 了。跑完 d i j k s t r a dijkstra dijkstra 后得到 d i s 2 dis_2 dis2,将 d i s 2 x dis_{2_x} dis2x 修改为 d i s 2 x − d i s 1 s + d i s 1 x dis_{2_x}-dis_{1_s}+dis_{1_x} dis2xdis1s+dis1x 即可修正过来。

可以自己画图琢磨一下,更有助于理解。


Code

#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#define N 10005
using namespace std;
int n,m,q;
int u[N],v[N];
vector<pair<int,int> >e[N],E[N];
int d1[N],d2[N],vis[N];
void spfa(){
	int x,y,w;
	memset(d1,0x3f,sizeof(d1));
	queue<int>Q;Q.push(0);d1[0]=0;
	while(!Q.empty()){
		x=Q.front(),Q.pop(),vis[x]=0;
		for(int i=0;i<e[x].size();++i){
			y=e[x][i].first,w=e[x][i].second;
			if(d1[y]>d1[x]+w){
				d1[y]=d1[x]+w;
				if(!vis[y])  vis[y]=1,Q.push(y);
			}
		}
	}
}
void dijkstra(int S){
	int x,y,w;
	memset(d2,0x3f,sizeof(d2));
	priority_queue<pair<int,int> >Q;
	Q.push(make_pair(0,S)),d2[S]=0;
	while(!Q.empty()){
		x=Q.top().second,Q.pop();
		for(int i=0;i<E[x].size();++i){
			y=E[x][i].first,w=E[x][i].second;
			if(d2[y]>d2[x]+w){
				d2[y]=d2[x]+w;
				Q.push(make_pair(-d2[y],y));
			}
		}
	}
	for(int i=1;i<=n;++i)  if(d2[i]!=0x3f3f3f3f)  d2[i]=d2[i]-d1[S]+d1[i];
}
vector<int>Tree[N];
int fa[N],dep[N],son[N],top[N],Size[N],ans[N];
void Clear(){
	memset(vis,0,sizeof(vis));
	memset(son,0,sizeof(son));
	for(int i=1;i<=n;++i)  Tree[i].clear();
}
void build(int S,int x){
	for(int i=0;i<e[x].size();++i){
		int y=e[x][i].first,w=e[x][i].second;
		if(d2[y]==d2[x]+w&&!vis[y]){
			vis[y]=1;
			Tree[x].push_back(y);
			build(S,y);
		}
	}
}
void dfs1(int x){
	Size[x]=1;
	for(int i=0;i<Tree[x].size();++i){
		int to=Tree[x][i];
		fa[to]=x,dep[to]=dep[x]+1;
		dfs1(to),Size[x]+=Size[to];
		if(Size[son[x]]<Size[to])  son[x]=to;
	}
}
void dfs2(int x,int tp){
	top[x]=tp;
	if(son[x])  dfs2(son[x],tp);
	for(int i=0;i<Tree[x].size();++i){
		int to=Tree[x][i];
		if(to!=son[x])  dfs2(to,to);
	}
}
int LCA(int x,int y){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])  swap(x,y);
		x=fa[top[x]];
	}
	return (dep[x]>dep[y])?y:x;
}
int main(){
	int x,y,z;
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=m;++i){
		scanf("%d%d%d",&x,&y,&z);
		e[x].push_back(make_pair(y,z));
	}
	for(int i=1;i<=n;++i)  e[0].push_back(make_pair(i,0));
	spfa();
	for(int i=1;i<=n;++i)
		for(int j=0;j<e[i].size();++j)
			E[i].push_back(make_pair(e[i][j].first,e[i][j].second+d1[i]-d1[e[i][j].first]));
	for(int i=1;i<=n;++i)  sort(e[i].begin(),e[i].end());
	for(int i=1;i<=q;++i)  scanf("%d%d",&u[i],&v[i]);
	memset(ans,-1,sizeof(ans));
	for(int i=1;i<=n;++i){
		Clear(),fa[i]=0,dep[i]=1;
		dijkstra(i),build(i,i),dfs1(i),dfs2(i,i);
		for(int j=1;j<=q;++j){
			if(d2[u[j]]==0x3f3f3f3f)  continue;
			if(d2[v[j]]==0x3f3f3f3f)  continue;
			int lca=LCA(u[j],v[j]);
			ans[j]=max(ans[j],d2[lca]);
		}
	}
	for(int i=1;i<=q;++i)  printf("%d\n",ans[i]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值