CF1051F

题意:给出一个无向连通图,图中有(n)个点,(m)条边,m-n≤20,给出(q)个询问,每一个询问包含两个整数(u)(v),对于每一次询问,输出(u)(v)之间的最短路

(m-nle20) 说明图是很接近树的,但是这个图是树上有一些环

如果它是一棵树,维护一个前缀(h)(h_v)表示从根节点到(v)的距离,前缀和可以在一次(dfs)求出,对于树上两点距离,长度就是(large h_u+h_v-h_{lca(u,v)})

我们试着将(m)变大,(m=n)时,假设多的边连了(x,y),长度为(z),那么(u,v)间就会多了几条路,但是贡献的只有(u o x o y o v)(u o y o x o v)两条路,长度为(dis(u,x)+z+dis(v,y))(dis(v,x)+z+dis(v,y)),与原来长度比较,三个求最小值

现在有(m-n+1)个非树边,这时,(u,v)间的路更多

考虑枚举这个路径经过的某一点(i),把这条路径拆成(u o i)(i o v)两部分,那么什么时候(i)对更新(dis(u,v))有贡献呢

要跑一个最小生成树,这样就可以把(n-1)条树边搞出来

只需枚举((m-n+1))条非树边的最多(2(m-n+1))个端点,(dis(u,v))这条路,只有两种情况

1.没经过非树边,只经过树边,这种情况就是(dis(u,v))初始值

2.经过某条非树边,这条路径一定是经过这条非树边两端点

所以(dis(u,v))可以通过(dis(u,i)+dis(i,v))更新,因为是无向图,式子变成了(dis(i,u)+dis(i,v)),所以跑(i)点开始的单源最短路,又因为(m-nle20),所以(i)最多(42)个,所以最多跑(42)遍最短路即可

#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
using namespace std;
#define ll long long
#define int long long
const int N = 2e5 + 25;
inline int read(){
	int x = 0;char c = getchar();
	while(!isdigit(c)) c = getchar();
	while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return x;
}
int n,m,Q;
int dep[N],fa[N][21],happen[N],vis[N];
ll dis[50][N],Tdis[N];
vector<int>p;
struct edge{ll u,v,w,f,next;}e[N<<1]; int head[N],tot;
inline void add(int u,int v,int z){
	e[tot] = (edge){u,v,z,0,head[u]}; head[u] = tot++;
}
void dfs(int x, int _fa) {
    vis[x] = 1; dep[x] = dep[_fa] + 1;
    for(int i = head[x]; ~i; i = e[i].next) {
        int to = e[i].v;
        if(vis[to]) continue;
        e[i].f = e[i ^ 1].f = 1;
        Tdis[to] = Tdis[x] + (ll)e[i].w;
        fa[to][0] = x;
        dfs(to, x);
    }
}
void Pre() {
    for(int j = 1; j <= 20; j++)
        for(int i = 1; i <= n; i++)
            fa[i][j] = fa[fa[i][j - 1]][j - 1];
}
void dijkstra(int x, int id) {
    memset(dis[id], 0x7f, sizeof(dis[id])); dis[id][x] = 0;
    memset(vis, 0, sizeof(vis));
    priority_queue<pair<int,int> > q; q.push(make_pair(0, x));
    while(!q.empty()) {
        int p = q.top().second; q.pop();
        if(vis[p]) continue;
        for(int i = head[p]; ~i; i = e[i].next) {
            int to = e[i].v;
            if(dis[id][to] > dis[id][p] + e[i].w && (!vis[to]))
                dis[id][to] = dis[id][p] + e[i].w, q.push(make_pair(-dis[id][to], to));
        }
    }
}
inline int lca(int x, int y) {
    if(dep[x] < dep[y]) swap(x, y);
    for(int i = 20; i >= 0; --i)
        if(dep[fa[x][i]] >= dep[y]) x = fa[x][i];
    if(x == y) return x;
    for(int i = 20; i >= 0; --i)
        if(fa[x][i] != fa[y][i])
            x = fa[x][i], y = fa[y][i];
    return fa[x][0];
}
signed main(){
	memset(head,-1,sizeof(head));
	n = read(); m = read();
    for(int i = 1; i <= m; i++) {
        int x = read(), y = read(), z = read();
        add(x, y, z);
        add(y, x, z);
    }
    dfs(1, 0);
    for(int i = 0; i < tot; i++)
        if(!e[i].f) {
            if(!happen[e[i].u]) p.push_back(e[i].u), happen[e[i].u] = 1;
            if(!happen[e[i].v]) p.push_back(e[i].v), happen[e[i].v] = 1;
        }

    for(int i = 0; i < p.size(); i++)
        dijkstra(p[i], i);
    Pre();
    int Q = read();
    while(Q--) {
        int x = read(), y = read();
        ll ans = Tdis[x] + Tdis[y] - 2 * Tdis[lca(x, y)];
        for(int i = 0; i < p.size(); i++)
            ans = min(ans, dis[i][x] + dis[i][y]);
        cout << ans << endl;
    }
}
原文地址:https://www.cnblogs.com/shikeyu/p/13904304.html