zoukankan      html  css  js  c++  java
  • [BZOJ2125]最短路

    2125: 最短路

    Time Limit: 1 Sec  Memory Limit: 259 MB Submit: 985  Solved: 411 [Submit][Status][Discuss]

    Description

    给一个N个点M条边的连通无向图,满足每条边最多属于一个环,有Q组询问,每次询问两点之间的最短路径。

    Input

    输入的第一行包含三个整数,分别表示N和M和Q 下接M行,每行三个整数v,u,w表示一条无向边v-u,长度为w 最后Q行,每行两个整数v,u表示一组询问

    Output

    输出Q行,每行一个整数表示询问的答案

    Sample Input

    9 10 2
    1 2 1
    1 4 1
    3 4 1
    2 3 1
    3 7 1
    7 8 2
    7 9 2
    1 5 3
    1 6 4
    5 6 1
    1 9
    5 7

    Sample Output

    5
    6

    HINT

    对于100%的数据,N<=10000,Q<=10000

    学习了一波圆方树,大概做法就是把在原仙人掌上的点设为圆点,然后对于每个环,把这个环上除了环顶的所有点重新连在一个方点下面,边权为到环顶的最短距离,方点连在环顶下,边权为0,其它的点该怎么连就怎么连,可以证明这样构成的树是和原仙人掌等价的

    那么求两点最短距离就可以搬用树上两点距离的做法,先求出lca。如果lca是圆点,那么直接就是路径上边权和,如果lca是方点,路径肯定还包含了环上的一段,那么就找到lca下方的两个点,根据环的总长度以及两个点分别到环顶的距离和最短距离方向可算出环上的一段

    求环用Tarjan的点双算法,求lca用的倍增,感觉这样好找lca下方的两点

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    char buf[10000000], *ptr = buf - 1;
    inline int readint(){
        int n = 0;
        char ch = *++ptr;
        while(ch < '0' || ch > '9') ch = *++ptr;
        while(ch <= '9' && ch >= '0'){
            n = (n << 1) + (n << 3) + ch - '0';
            ch = *++ptr;
        }
        return n;
    }
    const int maxn = 20000 + 10, maxm = 50000 + 10;
    struct Edge{
        int to, val, next;
        Edge(){}
        Edge(int _t, int _v, int _n){
            to = _t;
            val = _v;
            next = _n;
        }
    }e[maxm];
    int fir[maxn] = {0}, Fir[maxn] = {0}, cnt = 1;
    inline void ins(int u, int v, int w){
        e[++cnt] = Edge(v, w, fir[u]); fir[u] = cnt;
        e[++cnt] = Edge(u, w, fir[v]); fir[v] = cnt;
    }
    inline void link(int u, int v, int w){
        e[++cnt] = Edge(v, w, Fir[u]); Fir[u] = cnt;
    }
    int n, m, q, N;
    int dfn[maxn] = {0}, low[maxn], Index = 0;
    int sta[maxn], top = 0;
    int tmp[maxn], dir[maxn], val[maxn], tot[maxn];
    void tarjan(int u, int f){
        dfn[u] = low[u] = ++Index;
        sta[++top] = u;
        for(int v, i = fir[u]; i; i = e[i].next){
            v = e[i].to;
            if(!dfn[v]){
                val[v] = e[i].val;
                tarjan(v, i ^ 1);
                low[u] = min(low[u], low[v]);
                if(low[v] > dfn[u]){
                    link(u, v, e[i].val);
                    top--;
                }
                else if(low[v] == dfn[u]){
                    link(u, ++N, 0);
                    int c = 0, ss;
                    do{
                        tmp[++c] = sta[top];
                    }while(sta[top--] != v);
                    for(int j = fir[tmp[1]]; j; j = e[j].next)
                        if(e[j].to == u){
                            ss = tot[N] = e[j].val;
                            break;
                        }
                    for(int j = 1; j <= c; j++) tot[N] += val[tmp[j]];
                    for(int j = 1; j <= c; j++){        
                        if(ss < tot[N] - ss){
                            link(N, tmp[j], ss);
                            dir[tmp[j]] = 1;
                        }
                        else{
                            link(N, tmp[j], tot[N] - ss);
                            dir[tmp[j]] = 2;
                        }
                        ss += val[tmp[j]];
                    }
                }
            }
            else if(i ^ f) low[u] = min(low[u], dfn[v]);
        }
    }
    int dep[maxn], fa[maxn][16], road[maxn][16];
    void dfs(int u){
        for(int t = 1; (1 << t) <= dep[u]; t++){
            fa[u][t] = fa[fa[u][t - 1]][t - 1];
            road[u][t] = road[u][t - 1] + road[fa[u][t - 1]][t - 1];
        }
        for(int v, i = fir[u]; i; i = e[i].next){
            v = e[i].to;
            fa[v][0] = u;
            road[v][0] = e[i].val;
            dep[v] = dep[u] + 1;
            dfs(v);
        }
    }
    inline int Query(){
        int x = readint();
        int y = readint();
        if(dep[x] < dep[y]) swap(x, y);
        int d = dep[x] - dep[y], ans = 0;
        for(int t = 0; t < 16; t++)
            if(d & (1 << t)){
                ans += road[x][t];
                x = fa[x][t];
            }
        if(x == y) return ans;
        for(int t = 15; ~t; t--)
            if(fa[x][t] != fa[y][t]){
                ans += road[x][t] + road[y][t];
                x = fa[x][t];
                y = fa[y][t];
            }
        if(fa[x][0] <= n) return ans + road[x][0] + road[y][0];
        else if(dir[x] == dir[y]) return ans + abs(road[x][0] - road[y][0]);
        else return ans + min(road[x][0] + road[y][0], tot[fa[x][0]] - road[x][0] - road[y][0]);
    }
    int main(){
        fread(buf, sizeof(char), sizeof(buf), stdin);
        N = n = readint();
        m = readint();
        q = readint();
        for(int u, v, w, i = 1; i <= m; i++){
            u = readint();
            v = readint();
            w = readint();
            ins(u, v, w);
        }
        tarjan(1, 0);
        for(int i = 1; i <= N; i++) fir[i] = Fir[i];
        dep[1] = 0;
        dfs(1);
        for(int i = 1; i <= q; i++) printf("%d
    ", Query());
        return 0;
    }
  • 相关阅读:
    TeamX 专为中小团队思考的...团队协作工具
    8 月直播课抢先看 | 代码质量实战 + 微服务项目实战课程报名中
    CODING DevOps 代码质量实战系列第一课,本周开讲!
    CODING 现已支持墨刀原型引入
    CODING 企业微信小程序上线了
    CODING DevOps + Nginx-ingress 实现自动化灰度发布
    第二届腾讯运维技术开放日来啦!
    前端智造,内容新生
    kafka的特性初探
    弄懂一致性哈希后我打通了redis分区集群的原理
  • 原文地址:https://www.cnblogs.com/ruoruoruo/p/7445303.html
Copyright © 2011-2022 走看看