https://codeforc.es/contest/1051/problem/F
题意:
给一个联通simply图,n(1e5)个点,m条边,m-n<=20,给q(1e5)个询问,每个询问给ui和vi,求ui和vi之间的最短路。
思路:
发现边少,那么我们在它的树形图上思考(并查集处理出来任意树形图,不考虑多的m-n+1条边),那么任意两点距离就是dis[u]+dis[v]-2*dis[lca(u,v)]。
那么多了这m-n+1条边,相当多最了多了42个点,那么枚举给的询问经过这些点能否得到更小的答案。
那么对这42个点跑一边最短路。ans=max(树上的最短路,经过多出来的边连接的点的最短路)。
结论:处理出来x 的单源最短路可得到所有经过x的最短路。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define FI first
#define SE second
#define MP make_pair
#define PII pair<int,int>
const LL mod = 1e9+7;
const int MX = 1e5+5;
struct no{LL w,v;};
bool operator<(const no &x,const no &y){return x.w>y.w;}
int fa[MX];int found(int x){if(fa[x]==x)return x;return fa[x]=found(fa[x]);}
int n,m;
vector<no>g[MX],T[MX];
vector<int>v;map<int,int>id;
LL di[50][MX],bei[MX][22],de[MX],dis[MX];
void dij(int s){
for(int i=1;i<=n;i++) di[id[s]][i]=LLONG_MAX/2;
priority_queue<no>pq;
pq.push(no{0,s});di[id[s]][s]=0;
while(!pq.empty()){
int now=pq.top().v;pq.pop();
for(no i:g[now]){
LL zz=di[id[s]][now]+i.w;
if(zz<di[id[s]][i.v]) di[id[s]][i.v]=zz,pq.push(no{zz,i.v});
}
}
}
void dfs(int v,int p){//所有都是向上走2^k步 所代表的含义
for(auto i:T[v]) {
if(i.v!=p){
de[i.v]=de[v]+1;bei[i.v][0]=v;dis[i.v]=dis[v]+i.w;
dfs(i.v,v);
}
}
}
void initLCA(int nn){
bei[1][0]=-1;dfs(1,-1);
for(int k=0;k<=20;k++){
for(int v=1;v<=nn;v++){
if(bei[v][k]<0)bei[v][k+1]=-1;
else bei[v][k+1]=bei[bei[v][k]][k];
}
}
}
int getLCA(int u,int v){
if(de[u]>de[v]) swap(u,v);
for(int k=0;k<22;k++){
if((de[v]-de[u])>>k&1) v=bei[v][k];
}
if(u==v) return u;
for(int k=21;k>=0;k--){
if(bei[u][k]!=bei[v][k]){u=bei[u][k],v=bei[v][k];}
}
return bei[u][0];
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=m;i++){
int ui,vi,di;scanf("%d%d%d",&ui,&vi,&di);
int fx=found(ui),fy=found(vi);
if(fx==fy) v.push_back(ui),v.push_back(vi);
else T[ui].push_back(no{di,vi}),T[vi].push_back({di,ui}),fa[fx]=fa[fy];
g[ui].push_back(no{di,vi}),g[vi].push_back({di,ui});
}
sort(v.begin(),v.end());v.erase(unique(v.begin(),v.end()),v.end());
for(int i=0;i<(int)v.size();i++)id[v[i]]=i;
for(auto i:v)dij(i);
initLCA(n);
int Q;cin>>Q;while(Q--){
int ui,vi;scanf("%d%d",&ui,&vi);
int lca=getLCA(ui,vi);
LL ans=dis[ui]+dis[vi]-2*dis[lca];
for(int i=0;i<(int)v.size();i++){
ans=min(ans,di[i][ui]+di[i][vi]);
}
printf("%I64d\n",ans);
}
return 0;
}