CF-1051-F-最短路+最小生成树

1051-F 题目大意

给定一个\(n\)个点\(m\)条边的无向联通图,边带权。有\(q\)次询问,每次询问两点\(x,y\)直接的最短路的长度。


Solution

注意到\(m-n{\le}20\),那么整个图可以视为一个生成树加上不超过\(21\)条非树边构成的图,这些非树边构成一个边集\(E\)

先把整个图的最小生成树搞出来。考虑两点\(x,y\)之间的最短路,分为两种:

  • 一种是不经过非树边的最短路,这里用求\(LCA\)的方法可以得出。
  • 一种是经过非树边的最短路,可以枚举边集\(E\)中的边\(e\),边\(e\)连接的两点为\(u,v\),边权为\(w\),那么点\(x,y\)经过\(e\)的最短路的长度为

\[min(dis_{u-x}+dis_{v-y},dis_{v-x}+dis_{u-y})+w \]

这就需要我们预处理出所有边集\(E\)连接的点在原图上的最短路,用\(dijkstra\)解决即可。

二者取最小值即为询问的答案,时间复杂度为\(O(mlogm+nlogn+(m-n)mlogn+q(m-n+logn))\)

#include<bits/stdc++.h>
using namespace std;
using ll=long long;

template<typename T>
struct DSU{
	int n;
	vector<T> p,siz;
	DSU(int _n):p(_n+1),siz(_n+1),n(_n){
		iota(p.begin(),p.end(),0);
		for(int i=0;i<=n;i++) siz[i]=1;
	}
	T findd(T x){
		return p[x]==x?x:p[x]=findd(p[x]);
	}
	void unionn(T x,T y){
		x=findd(x),y=findd(y);
		if(x==y) return;
		if(siz[x]>siz[y]) swap(x,y);
		p[x]=y;
		siz[y]+=siz[x];
	}
};

void solve(){
    int n,m;
    cin>>n>>m;
    vector<array<int,3>> ed(m);
    vector<array<int,3>> other;
    for(int i=0;i<m;i++){
        int x,y,w;
        cin>>x>>y>>w;
        x--,y--;
        ed[i]={x,y,w};
    }
    sort(ed.begin(),ed.end(),[](const auto &a,const auto &b){
        return a[2]<b[2];
    });
    DSU<int> dsu(n);
    vector<vector<pair<int,int>>> g(n),e(n);
    for(int i=0;i<m;i++){
        auto [x,y,w]=ed[i];
        if(dsu.findd(x)!=dsu.findd(y)){
            dsu.unionn(x,y);
            e[x].push_back({y,w});
            e[y].push_back({x,w});
        }else{
            other.push_back(ed[i]);
        }
        g[x].push_back({y,w});
        g[y].push_back({x,w});
    }
    int P=18;
    vector<ll> pre(n);
    vector<int> dep(n);
    vector<vector<int>> fa(n,vector<int>(P));
    function<void(int,int)> dfs=[&](int x,int father){
        fa[x][0]=father;
        for(int i=1;i<P;i++){
            fa[x][i]=fa[fa[x][i-1]][i-1];
        }
        for(auto [y,w]:e[x]){
            if(y==father) continue;
            pre[y]=pre[x]+w;
            dep[y]=dep[x]+1;
            dfs(y,x);
        }
    };
    dfs(0,0);
    function<int(int,int)> lca=[&](int x,int y){
        if(dep[x]<dep[y]) swap(x,y);
        for(int i=P-1;~i;i--){
            if(dep[fa[x][i]]>=dep[y]){
                x=fa[x][i];
            }
        }
        if(x==y) return x;
        for(int i=P-1;~i;i--){
            if(fa[x][i]!=fa[y][i]){
                x=fa[x][i],y=fa[y][i];
            }
        }
        return fa[x][0];
    };
    vector<vector<ll>> dis(n);
    function<void(int)> dijkstra=[&](int s){
        dis[s]=vector<ll>(n,1e16);
        vector<int> vis(n);
        dis[s][s]=0;
        priority_queue<pair<ll,int>,vector<pair<ll,int>>,greater<>> q;
        q.push({0,s});
        while(!q.empty()){
            auto [dx,x]=q.top();
            q.pop();
            if(vis[x]) continue;
            vis[x]=1;
            for(auto [y,w]:g[x]){
                if(!vis[y]&&dis[s][x]+w<dis[s][y]){
                    dis[s][y]=dis[s][x]+w;
                    q.push({dis[s][y],y});
                }
            }
        }
    };
    for(auto [x,y,w]:other){
        if(!dis[x].size()){
            dijkstra(x);
        }
        if(!dis[y].size()){
            dijkstra(y);
        }
    }
    int q;
    cin>>q;
    while(q--){
        int x,y;
        cin>>x>>y;
        x--,y--;
        ll res=pre[x]+pre[y]-2*pre[lca(x,y)];
        for(auto [u,v,w]:other){
            res=min({res,dis[u][x]+dis[v][y]+w,dis[u][y]+dis[v][x]+w});
        }
        cout<<res<<'\n';
    }
}

int main(){
    ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int T=1;
    //cin>>T;
    while(T--){
        solve();
    }
    return 0;
}
posted @ 2024-01-17 21:40  fengxue-K  阅读(18)  评论(0编辑  收藏  举报