P1041 我们最好的最短路 官方题解
Gioush OJ · P1041 我们最好的最短路
我们最好的最短路
【题意简述】
给出一张 个点、 条边的连通无向图,满足 。多次询问两点之间的最短路。
【Hint】
【提示】
任取一棵生成树,非树边只有常数条。把所有非树边端点作为关键点,从每个关键点运行一次 Dijkstra。
【数据点 1∼21\sim 21∼2】
使用 Floyd 预处理任意两点之间的最短路。时间复杂度为 ,空间复杂度为 。
【数据点 3∼43\sim 43∼4】
图是一棵树。倍增预处理最近公共祖先,并记录根到每个结点的距离 。树上两点距离为
【数据点 5∼65\sim 65∼6】
询问数较小时,可以对每次询问从起点运行 Dijkstra。时间复杂度为 。
【正解】
从原图中任取一棵生成树 。非树边数量为
把所有非树边的两个端点放入集合 ,则 。
先在生成树上倍增预处理最近公共祖先,从而支持树上距离 。再从每个关键点 出发,在原图上运行一次 Dijkstra,记到结点 的最短距离为 。
对于询问 ,答案为
若一条最短路不经过非树边,那么它完全位于生成树中,只能是树上 到 的唯一简单路径,第一项得到答案。
若一条最短路经过非树边,那么它必然经过某条非树边的端点 。沿最短路在 处分开,有
左侧本身对应一条从 经过 到 的合法路线,又不可能小于全局最短路,所以该关键点给出的候选恰好等于答案。
【复杂度分析】
总时间复杂度为
空间复杂度为 。边权与路径长度需要使用 long long。
【参考代码】
#include <queue>#include <vector>#include <cstdio>#include <cstring>#include <algorithm>#define MAXN 100010#define MAXK 45using namespace std; int c,n,m,q,In1,In2,In3,Head[MAXN],TreeHead[MAXN],Total=0,TreeTotal=0;int SetFa[MAXN],Size[MAXN],Fa[MAXN][22],Dep[MAXN],Key[MAXK],cnt=0;long long Dis[MAXN],Dist[MAXK][MAXN];bool Mark[MAXN]; struct edge { int Ed,Next; long long Val;}Edge[MAXN<<1],Tree[MAXN<<1]; void Edge_Add(int St,int Ed,int Val){Edge[++Total]={Ed,Head[St],Val};Head[St]=Total;}void Tree_Add(int St,int Ed,int Val){Tree[++TreeTotal]={Ed,TreeHead[St],Val};TreeHead[St]=TreeTotal;}int Find(int x){return SetFa[x]==x?x:SetFa[x]=Find(SetFa[x]);}void Insert(int x){if (!Mark[x]) Mark[x]=1,Key[++cnt]=x;} void Build() { queue <int> Q; Dep[1]=1;Q.push(1); while (Q.size()) { int Now=Q.front();Q.pop(); for (int i=TreeHead[Now];i;i=Tree[i].Next) { int To=Tree[i].Ed; if (To==Fa[Now][0]) continue; Fa[To][0]=Now;Dep[To]=Dep[Now]+1; Dis[To]=Dis[Now]+Tree[i].Val; for (int j=1;j<=21;j++) Fa[To][j]=Fa[Fa[To][j-1]][j-1]; Q.push(To); } }} int LCA(int x,int y) { if (Dep[x]<Dep[y]) swap(x,y); for (int i=21;i>=0;i--) if (Dep[Fa[x][i]]>=Dep[y]) x=Fa[x][i]; if (x==y) return x; for (int i=21;i>=0;i--) { if (Fa[x][i]!=Fa[y][i]) x=Fa[x][i],y=Fa[y][i]; } return Fa[x][0];} long long Query(int x,int y) { int lca=LCA(x,y); return Dis[x]+Dis[y]-2*Dis[lca];} priority_queue<pair<long long,int>,vector<pair<long long,int>>,greater<pair<long long,int>>> Q; void Dijkstra(int x,int S) { memset(Dist[x],0x3f,sizeof(Dist[x])); Dist[x][S]=0;Q.emplace(0,S); while (Q.size()) { long long Value=Q.top().first; int Now=Q.top().second;Q.pop(); if (Value!=Dist[x][Now]) continue; for (int i=Head[Now];i;i=Edge[i].Next) { int To=Edge[i].Ed; if (Dist[x][To]>Dist[x][Now]+Edge[i].Val) { Dist[x][To]=Dist[x][Now]+Edge[i].Val; Q.emplace(Dist[x][To],To); } } }} int main() {#ifndef ONLINE_JUDGE freopen("shortest.in","r",stdin); freopen("shortest.out","w",stdout);#endif scanf("%d",&c); scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) SetFa[i]=i,Size[i]=1; for (int i=1;i<=m;i++) { scanf("%d%d%d",&In1,&In2,&In3); Edge_Add(In1,In2,In3);Edge_Add(In2,In1,In3); int x=Find(In1),y=Find(In2); if (x!=y) { if (Size[x]>Size[y]) swap(x,y); SetFa[x]=y;Size[y]+=Size[x]; Tree_Add(In1,In2,In3);Tree_Add(In2,In1,In3); } else Insert(In1),Insert(In2); } Build(); for (int i=1;i<=cnt;i++) Dijkstra(i,Key[i]); scanf("%d",&q); while (q-->0) { scanf("%d%d",&In1,&In2); long long Ans=Query(In1,In2); for (int i=1;i<=cnt;i++) Ans=min(Ans,Dist[i][In1]+Dist[i][In2]); printf("%lld\n",Ans); } return 0;}