题目链接
思路:由于m-n小于20,两点之间的最短路只有两种情况。1、两点的最短路不经过非树边,那么最短路用lca求就行了,2、如果至少经过一条非树边呢?这题精华就在这儿,由于m-n小于20,最多只有40个点连这非树边,于是对这些点求spfa,最多执行40遍spfa,再进行比较就行了,这里说一下经过非树边的最短路是d【i】【x】+d【i】【y】,表示的是以i为顶点到x的最短距离+到y的最短距离。
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+1;
int n,deep[maxn],parent[maxn][26];
vector<pair<int,ll> >g[maxn];
vector<int>p;
ll w,weight[maxn],d[44][maxn];
bool vis[maxn];
void dfs(int u,int fa)
{
deep[u]=deep[fa]+1;
parent[u][0]=fa;
for(auto to:g[u])
{
if(to.first==fa) continue;
if(!deep[to.first]) weight[to.first]=weight[u]+to.second,dfs(to.first,u);
else p.push_back(u),p.push_back(to.first);
}
}
void init()
{
for(int k=1;k<=21;++k)
for(int i=1;i<=n;++i)
parent[i][k]=parent[parent[i][k-1]][k-1];
}
int lca(int u,int v)
{
if(deep[u]<deep[v]) swap(u,v);
if(u!=v)
for(int i=21;i>=0;--i) if(deep[parent[u][i]]>=deep[v]) u=parent[u][i];
if(u==v) return u;
for(int i=21;i>=0;--i)
if(parent[u][i]!=parent[v][i]) u=parent[u][i],v=parent[v][i];
return parent[u][0];
}
ll getdis(int x,int y)
{
return weight[x]+weight[y]-2*weight[lca(x,y)];
}
void spfa(int x,int id)
{
memset(vis,false,sizeof(vis));
memset(d[id],0x3f3f3f3f,sizeof(d[id]));
d[id][x]=0;
queue<int>q;
q.push(x);
vis[x]=true;
while(!q.empty())
{
int top=q.front();
q.pop();
vis[top]=false;
for(auto v:g[top])
{
if(d[id][top]+v.second<d[id][v.first])
{
d[id][v.first]=d[id][top]+v.second;
if(!vis[v.first]) q.push(v.first),vis[v.first]=true;
}
}
}
}
int main()
{
int m,u,v,q,x,y;
scanf("%d %d",&n,&m);
for(int i=1;i<=m;++i)
{
scanf("%d %d %lld",&u,&v,&w);
g[u].push_back({v,w});
g[v].push_back({u,w});
}
weight[1]=0,deep[1]=1;
dfs(1,0);
init();
sort(p.begin(),p.end());
p.erase(unique(p.begin(),p.end()),p.end());
for(int i=0;i<p.size();++i) spfa(p[i],i);
scanf("%d",&q);
while(q--)
{
scanf("%d %d",&x,&y);
ll ans=getdis(x,y);
for(int i=0;i<p.size();++i)
ans=min(ans,d[i][x]+d[i][y]);
printf("%lld\n",ans);
}
}