• 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;
        }
    }
    
  • 相关阅读:
    Mybatis
    spring2
    servlet
    MyBatis1
    Docker容器保存为镜像文件并发布到DockerHub上
    【JS】提取字符串中的分数,汇总后算出平均分,并与每个分数比较,输出
    CentOS下创建管理员权限用户
    Centos安装文件传输软件rz sz
    2022年的零日漏洞影响了哪些平台?
    关于渗透测试的优缺点,你知道吗?
  • 原文地址:https://www.cnblogs.com/shikeyu/p/13904304.html
Copyright © 2020-2023  润新知