http://codeforces.com/contest/1051/problem/F
题意:
给你n个点,m条边。m-n<=20。保证图连通。
问你任意两点的最短距离是多少。
POINT:
由图连通可知,这个图是 在生成树的基础上加了m-(n-1)条边。然后问你距离。
在HDU多校做过一道类似的。是不过只增加了一条边。即m=n。那么我们只要考虑两种情况:
1.在树上的距离查询(不用这条边)2.假设这条边连了AB。考虑起点到A(B)+边权+B(A)到终点的距离。
这样取min就是答案。不过那道题还有修改操作,就要用到树链剖分。
类推得。这道题是多了m-(n-1)条边。因为没有修改操作。我们可以对这些多余的边所连的点(最多42个点)分别跑一次最短路。这样经过这些多余的边的距离肯定包含在了这多次最短路里面。
然后还有一种情况就是不走这些多余的边。那么就是树上任意两点的距离查询。
记录每个点到根节点的距离。dis(a,b)=dis(a,root)+dis(b,root)-2*dis(lca(a,b),root)。
#include <iostream>
#include <stdio.h>
#include <queue>
#include <vector>
#include <math.h>
#include <algorithm>
using namespace std;
#define LL long long
const int N = 2e5+4;
const LL inf = 0x3f3f3f3f3f3f3f3f;
vector<int>G[N];
vector<LL>W[N];
LL dis[50][N],disRoot[N];
int d[N],fa[N][33],vis[N];
vector<int>P;
void dfs(int u,int pre)
{
for(int i=1;i<=20;i++){
fa[u][i]=fa[fa[u][i-1]][i-1];
}
for(int i=0;i<G[u].size();i++){
int v=G[u][i];
if(v==pre) continue;
if(vis[v]) P.push_back(u),P.push_back(v);
else{
vis[v]=1;
d[v]=d[u]+1;
disRoot[v]=disRoot[u]+W[u][i];
fa[v][0]=u;
dfs(v,u);
}
}
}
int lca(int a,int b)
{
if(d[a]<d[b]) swap(a, b);
int dis=d[a]-d[b];
for(int i=0;i<20;i++){
if(dis&(1<<i)){
a=fa[a][i];
}
}
if(a==b) return a;
for(int i=20;i>=0;i--){
if(fa[a][i]!=fa[b][i])
a=fa[a][i],b=fa[b][i];
}
return fa[a][0];
}
int n,m;
void spfa(int x)
{
for(int i=1;i<=n;i++) dis[x][i]=inf,vis[i]=0;
int s=P[x];
vis[s]=1;dis[x][s]=0;
queue<int>q;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
vis[u]=0;
for(int i=0;i<G[u].size();i++){
int v=G[u][i];
if(dis[x][v]>dis[x][u]+W[u][i]){
dis[x][v]=dis[x][u]+W[u][i];
if(vis[v]==0){
vis[v]=1;
q.push(v);
}
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int u,v;LL w;
scanf("%d%d%lld",&u,&v,&w);
G[u].push_back(v);G[v].push_back(u);
W[u].push_back(w);W[v].push_back(w);
}
d[1]=1;vis[1]=1;disRoot[1]=0;
dfs(1,-1);
sort(P.begin(),P.end());P.erase(unique(P.begin(),P.end()),P.end());
for(int i=0;i<P.size();i++){
spfa(i);
}
int q;scanf("%d",&q);
while(q--){
int a,b;scanf("%d%d",&a,&b);
LL ans = disRoot[a]+disRoot[b]-2*disRoot[lca(a,b)];
for(int i=0;i<P.size();i++){
ans=min(ans,dis[i][a]+dis[i][b]);
}
printf("%lld\n",ans);
}
return 0;
}