传送门
大概题意
给你一个包含n个点m条无向边的图,多次询问最短路(保证每两个点都连通,m - n ≤ 20 )
思路
我们要注意题面上给你的条件( m - n ≤ 20 ),这是一个很重要的信息。我们知道含有n个结点的树包含 n - 1 条边,这道题相当于在树上又多加了几条边。如果给我们一棵树,多次询问最短路那么答案等于 dis[ s ] + dis [ e ] - 2 * dis[ lca ( s , e ) ]。当然我们的求解的最短路可能所有边都在树上,所以上面这种情况也是答案的一种。还有一种情况就是,最短路会经过非树边。这个问题我们解决呢?我们可以求解出所有非树边上面结点的最短路(最多42个点),相当于我们利用这一个点作为转折点。那么答案就可能等于
dis[ s - > 转折点] + dis [ 转折点 - > e ]。我们为什么只需要一个转折点呢?因为我们求解的是最短路,dis[ s - > 转折点] 和 dis [ 转折点 - > e ] 都是最优的情况,我们只需要取 所有转折点的最优情况和第一种情况 的最小值。
附上代码:
///#include<bits/stdc++.h>
///#include<unordered_map>
///#include<unordered_set>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<new>
#include<vector>
#define MT(a,b) memset(a,b,sizeof(a));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pai=acos(-1.0);
const double E=2.718281828459;
const ll mod=1e9+7;
const ll INF=0x3f3f3f3f3f3f;
int n,m,op;
struct node
{
int e;
ll c;
int p;
int vis;
bool friend operator<(node a,node b)
{
return a.c>b.c;
}
}load[200005];
int head[100005],sign;
bool flag[100005];
void add_edge(int s,int e,ll c)
{
load[++sign]=node{e,c,head[s],0};
head[s]=sign;
}
int grand[100005][20],N;
int depth[100005];
ll dis[100005];
void dfs(int s)
{
flag[s]=1;
for(int i=1;i<=N;i++)
grand[s][i]=grand[grand[s][i-1]][i-1];
for(int i=head[s];~i;i=load[i].p)
{
int e=load[i].e;
if(!flag[e])
{
///将这条边标记
load[i].vis=1;
(i&1)?load[i+1].vis=1:load[i-1].vis=1;
depth[e]=depth[s]+1;
grand[e][0]=s;
dis[e]=dis[s]+load[i].c;
dfs(e);
}
}
}
int get_lca(int a,int b)
{
if(depth[a]>depth[b])
swap(a,b);
for(int i=N; i>=0; i--)
{
if(depth[b]>=depth[a]&&depth[grand[b][i]]>=depth[a])
b=grand[b][i];
}
for(int i=N; i>=0; i--)
{
if(grand[a][i]!=grand[b][i])
{
a=grand[a][i];
b=grand[b][i];
}
}
return a==b?a:grand[b][0];
}
int add[100],up;
ll shorts[50][100005];///最多42个点,千万不要开小了
void dij(int sub,int s)
{
shorts[sub][s]=0;
bool vis[100005];
memset(vis,0,sizeof(vis));
priority_queue<node>q;
q.push(node{s,0});
while(!q.empty())
{
node w=q.top();
s=w.e;
q.pop();
if(vis[s])
continue;
vis[s]=1;
for(int i=head[s];~i;i=load[i].p)
{
int e=load[i].e;
if(!vis[e]&&shorts[sub][e]>shorts[sub][s]+load[i].c)
{
shorts[sub][e]=shorts[sub][s]+load[i].c;
q.push(node{e,shorts[sub][e]});
}
}
}
}
void init()
{
N=log2(n);
up=sign=0;
memset(head,-1,sizeof(head));
memset(grand,0,sizeof(grand));
memset(depth,0,sizeof(depth));
memset(dis,0,sizeof(dis));
memset(flag,0,sizeof(flag));
fill(shorts[0],shorts[0]+sizeof(shorts)/sizeof(ll),INF);
depth[0]=-1;
}
int main()
{
int s,e;
long long c;
scanf("%d %d",&n,&m);
init();
for(int i=1;i<=m;i++)
{
scanf("%d %d %lld",&s,&e,&c);
add_edge(s,e,c);
add_edge(e,s,c);
}
dfs(1);
for(int i=1;i<=n;i++)
{
for(int j=head[i];~j;j=load[j].p)
{
///如果没有被标记,则说明是非树边
if(!load[j].vis)
{
add[++up]=i;
add[++up]=load[j].e;
}
}
}
///去重
sort(add+1,add+1+up);
int d=unique(add+1,add+1+up)-(add+1);
///最短路
for(int i=1;i<=d;i++)
dij(i,add[i]);
scanf("%d",&op);
while(op--)
{
scanf("%d %d",&s,&e);
ll ans=INF;
///第二种情况
for(int i=1;i<=d;i++)
ans=min(ans,shorts[i][s]+shorts[i][e]);
///第一种情况
int x=get_lca(s,e);
ans=min(ans,dis[s]+dis[e]-2*dis[x]);
printf("%lld\n",ans);
}
return 0;
}