这题如果注意到m的范围,那么思路将会豁然开朗
因为如果是一棵树,那么直接lca就能求得两点间最小值
但是现在是图,并且边数就比一棵树大100左右
所以我们可以想到,先将图中取出生成树,用lca求答案
那么剩下就有100条边左右未使用,我们发现,答案可能经过这些边,因此我们枚举这些边的端点去做01最短路,对两种情况取min就是最后答案
直接做最短路的正确性在于,如果答案使用到了某条未在树上的边,那么我们因为使用了这条边做最短路,因此我们求得的一定是通过这条边相关的最短距离
因此枚举全部就能获得答案
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<ll,ll> pll; const int N=1e5+200; const int mod=1e9+7; int h[N],ne[N<<1],e[N<<1],idx,cnt; bool st[N<<1],pos[N],used[N<<1]; int n,m; int lg[N<<1]; int depth[N],f[N][25],dis[210][N],from[N<<1],to[N<<1]; void add(int a,int b){ e[idx]=b,ne[idx]=h[a],h[a]=idx++; } void dfs(int u,int fa){ depth[u]=depth[fa]+1; st[u]=1; f[u][0]=fa; int i; for(i=1;i<lg[depth[u]];i++){ f[u][i]=f[f[u][i-1]][i-1]; } for(i=h[u];i!=-1;i=ne[i]){ int j=e[i]; if(st[j]) continue; used[i]=used[i^1]=1; dfs(j,u); } } void bfs(int s){ int i; cnt++; for(i=0;i<=n;i++){ dis[cnt][i]=0x3f3f3f3f; st[i]=0; } dis[cnt][s]=0; queue<int> q; q.push(s); while(q.size()){ int t=q.front(); q.pop(); if(st[t]) continue; st[t]=1; for(i=h[t];i!=-1;i=ne[i]){ int j=e[i]; if(dis[cnt][j]>dis[cnt][t]+1){ dis[cnt][j]=dis[cnt][t]+1; q.push(j); } } } } int lca(int a,int b){ if(depth[a]<depth[b]) swap(a,b); int i; for(i=21;i>=0;i--){ if(depth[f[a][i]]>=depth[b]){ a=f[a][i]; } } if(a==b) return a; for(i=21;i>=0;i--){ if(f[a][i]!=f[b][i]){ a=f[a][i]; b=f[b][i]; } } return f[a][0]; } int main(){ //ios::sync_with_stdio(false); memset(h,-1,sizeof h); cin>>n>>m; int i; for(i=1;i<=n;i++) lg[i]=lg[i-1]+(i==(1<<lg[i-1])); for(i=1;i<=m;i++){ int a,b; scanf("%d%d",&a,&b); from[idx]=a; add(a,b); from[idx]=b; add(b,a); } st[0]=1; dfs(1,0); for(i=0;i<idx;i+=2){ if(used[i]) continue; used[i]=used[i^1]=1; if(!pos[from[i]]){ pos[from[i]]=1; bfs(from[i]); } } int q; cin>>q; while(q--){ int a,b; scanf("%d%d",&a,&b); int ans; ans=depth[a]+depth[b]-2*depth[lca(a,b)]; for(i=1;i<=cnt;i++) ans=min(ans,dis[i][a]+dis[i][b]); printf("%d\n",ans); } return 0; }View Code