题目链接:点此跳转
题目大意:给一个连通图,每次询问两点间最短路。每条边的长度都是1。
第一行两个整数n和m,表示图的点数和边数(1≤ n≤ 100000, 1≤ m≤ n+100)。
解题思路:
因为这道题边的范围比较特别,1≤m≤n+100。我们先假设m恰好是n-1即图为一棵树,那么我们可以通过最近公共祖先求得两点的最近距离为dep[a]+dep[b]-2*dep[lca(a,b)]。现在又多出来100条边,我们要怎么处理呢?
答案是:bfs 直接对这些边的一个端点开始暴力搜索最短路即可。
Code:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=1e5+5;
int dp[N][25],f[N],dep[N],dis[120][N],head[N];
int tot,num;
vector<pii> ve;
struct node{
int now,to,net;
}s[N*2+100];
void add(int u,int v){ //链式前向星存图
s[++tot]={u,v,head[u]};
head[u]=tot;
s[++tot]={v,u,head[v]};
head[v]=tot;
}
int find(int x){ //最小生成树
if(f[x]==x) return x;
return f[x]=find(f[x]);
}
void dfs(int node,int fa){
dep[node]=dep[fa]+1;
dp[node][0]=fa;
for(int i=1;(1<<i)<=dep[node];i++){
dp[node][i]=dp[dp[node][i-1]][i-1];
}
for(int i=head[node];~i;i=s[i].net){
if(s[i].to==fa) continue;
dfs(s[i].to,node);
}
}
int lca(int x,int y){
if(dep[x]<dep[y])swap(x,y);
int tep=dep[x]-dep[y];
for(int j=0;tep;j++){
if(tep&1) x=dp[x][j];
tep>>=1;
}
if(x==y) return x;
for(int j=21;j>=0&&x!=y;--j){
if(dp[x][j]!=dp[y][j]){
x=dp[x][j];
y=dp[y][j];
}
}
return dp[x][0];
}
void bfs(int node){
num++;
dis[num][node]=0;
queue<int> q;
q.push(node);
while(!q.empty()){
int star=q.front(); q.pop();
for(int i=head[star];~i;i=s[i].net){
if(dis[num][s[i].to]>dis[num][star]+1){
dis[num][s[i].to]=dis[num][star]+1;
q.push(s[i].to);
}
}
}
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
memset(dis,0x3f3f,sizeof dis);
for(int i=1;i<=n;i++){
head[i]=-1;
f[i]=i;
}
for(int i=0;i<m;i++){
int u,v;
scanf("%d%d",&u,&v);
if(find(u)==find(v)) ve.push_back({u,v});
else{
f[find(u)]=find(v);
add(u,v);
}
}
dfs(1,0); // lca 预处理
for(auto k:ve){
add(k.first,k.second);
}
for(auto k:ve){ //求这个点到其他点的最短距离
bfs(k.first);
}
int q;
scanf("%d",&q);
while(q--){
int a,b;
scanf("%d%d",&a,&b);
int ans=dep[a]+dep[b]-2*dep[lca(a,b)];
for(int i=1;i<=num;i++){
ans=min(ans,dis[i][a]+dis[i][b]);
}
printf("%d\n",ans);
}
return 0;
}