题目描述
输入一张无向图,对于无向图的每条边u,v,w,将u和v转换成二进制后,u是v的前缀。
给出q次询问,每次输入s,t,求s到t的最短距离。
题解
从题目数据而言,n为1e5,m为2e5,显然一般的多源最短路算法无法完成。
考虑此题的特殊性质:由于边仅可能从u连向以u为前缀的v,那么若建立一颗以1为根的完全二叉树,u一定是v的祖先。
考虑以二叉树的形式维护答案:设dis[x][p]表示点x向上p层,得到的祖先到x的距离。
也就是说,对于任意的s和t,只需要找到s和t的最近公共祖先,然后从将该祖先根节点1路径上的所有点设为中转点o,计算s到o的距离和o到t的距离即可。
代码
#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);
}
}
标签:count,int,题解,ans,CCPC,2021,lca,dis
From: https://www.cnblogs.com/zwzww/p/18161099