Problem
在一张 n n n 个点 m m m 条边的有向图上,Alice 和 Bob 将要从共同的起点出发,沿最短路分别走到目标点 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(1≤k≤min(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,⋯,uk−1=vk−1,且 u k < v k u_k<v_k uk<vk。
- n < m n<m n<m,且 ∀ i ( 1 ≤ i ≤ n ) ∀i(1≤i≤n) ∀i(1≤i≤n),都有 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 1≤u,v≤n≤2000, 1 ≤ m , q ≤ 6000 1≤m,q≤6000 1≤m,q≤6000, 0 ≤ ∣ w ∣ ≤ 1 0 4 0≤|w|≤10^4 0≤∣w∣≤104,保证不存在负环。
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+dis1u−dis1v(改过后的 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} dis2x−dis1s+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;
}