天天看点

CF E51 F. The Shortest Statement//lca(倍增)+dijkstra+dsu ※

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;
}