[题解][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);
	}
}
posted @   ZWZWW  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示