[题解][2021浙江CCPC] Shortest Path Query
题目描述
输入一张无向图,对于无向图的每条边u,v,w,将u和v转换成二进制后,u是v的前缀。
给出q次询问,每次输入s,t,求s到t的最短距离。
题解
从题目数据而言,n为1e5,m为2e5,显然一般的多源最短路算法无法完成。
考虑此题的特殊性质:由于边仅可能从u连向以u为前缀的v,那么若建立一颗以1为根的完全二叉树,u一定是v的祖先。
也就是说,对于任意的s和t,只需要找到s和t的最近公共祖先,然后从将该祖先到根节点1路径上的所有点设为中转点o,计算distance(s,o)+distance(o,t)。
那么需要以每个点为起点,跑n次dijkstra,每次只遍历以起点为根的子树。
设dis[x][p]表示点x向上p层,得到的祖先到x的距离。
设count(u,v)为计算u在v上面几层,答案即为:dis[s][count(o,s)]+dis[t][count(o,t]。
时间复杂度约为O(n(logn)^2)。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=100007,M=200007;
int en=0;
int dis[N][20],vis[N],in[N],d[N],h[N];
struct Edge{
int v,w,nxt;
}e[M<<1];
void adde(int u,int v,int w){
e[++en].w=w,e[en].v=v,e[en].nxt=h[u],h[u]=en;
}
int count(int u,int v){
int res=0;
while(u<v){
res++;
v>>=1;
}
return res;
}
int LCA(int u,int v){
while(u!=v){
if(u>v)u>>=1;
else v>>=1;
}
return u;
}
void dijkstra(int s){
priority_queue< pair<int,int> >q;
q.push(make_pair(0,s));
d[s]=0,in[s]=s;
while(!q.empty()){
int x=q.top().second;
q.pop();
if(vis[x]==s)continue;
vis[x]=s;
dis[x][count(s,x)]=d[x];
for(int i=h[x];i;i=e[i].nxt){
int y=e[i].v,w=e[i].w;
if(y<s)continue;
if(in[y]!=s){
in[y]=s;
d[y]=1e16;
}
if(d[y]>d[x]+w){
d[y]=d[x]+w;
q.push(make_pair(-d[y],y));
}
}
}
}
void ask(int s,int t){
int ans=1e16;
int lca=LCA(s,t);
int ss=count(lca,s),tt=count(lca,t);
for(;lca;lca>>=1)ans=min(ans,dis[s][ss++]+dis[t][tt++]);
if(ans!=1e16)cout<<ans<<endl;
else cout<<-1<<endl;
}
signed main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v,w;
cin>>u>>v>>w;
adde(u,v,w),adde(v,u,w);
}
memset(dis,0x3f3f3f3f,sizeof dis);
for(int i=1;i<=n;i++)dis[i][0]=0;
for(int i=1;i<=n;i++)dijkstra(i);
int q;
cin>>q;
while(q--){
int s,t;
cin>>s>>t;
ask(s,t);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】