传送门


对仙人掌建立圆方树,然后对边定权

对于圆点和圆点之间的边,是原来仙人掌上的桥,边权保持不变

对于圆点和方点之间的边,将圆方树看做以一个圆点为根的有根树之后,一个方点的父亲一定是一个圆点。对于这条方圆边,将边权设为\(0\)。

而对于这个方点连接的其他圆点来说,如果要从这个点走到方点的父亲并走出这一个环,在原仙人掌上会走最短的路径。那么这些圆方边的权值就是在原仙人掌上从这个圆点到对应方点的父亲的最短路径长度。

然后在圆方树上建立倍增数组

接着考虑每一个询问。

对于某一个询问\((x,y)\),如果它们在圆方树上的\(LCA\)是圆点,那么直接取这一段路径的权值和。如果它们的\(LCA\)是方点,意味着通过不断跳,\(x\)和\(y\)跳到了同一个环内,那么在这个环内就有两条路径可以选择。那么我们现在要求环上的这两个点的最短路径。

考虑在建立圆方树时记录在\(dfs\)树上每一个点的带权深度,这样可以轻松地算出一个点到其方点父亲的最短路是否经过了返祖边,并通过这个计算出两个点之间的距离。

#include<bits/stdc++.h>
#define PII pair < int , int >
#define st first
#define nd second
//This code is written by Itst
using namespace std;

inline int read(){
    int a = 0;
    char c = getchar();
    bool f = 0;
    while(!isdigit(c) && c != EOF){
        if(c == '-')
            f = 1;
        c = getchar();
    }
    if(c == EOF)
        exit(0);
    while(isdigit(c)){
        a = a * 10 + c - 48;
        c = getchar();
    }
    return f ? -a : a;
}

const int MAXN = 2e4 + 7;
struct Edge{
    int end , upEd , w;
}Ed[MAXN << 1];
vector < int > ch[MAXN];
int head[MAXN] , dis[MAXN] , dep[MAXN] , jump[MAXN][16][2] , cir[MAXN];
int N , M , Q , cntEd , cnt;

inline void addEd(int a , int b , int c){
    Ed[++cntEd].end = b;
    Ed[cntEd].upEd = head[a];
    Ed[cntEd].w = c;
    head[a] = cntEd;
}

PII dfs1(int x , int p){
    dep[x] = dep[p] + 1;
    PII cur(0 , 0);
    for(int i = head[x] ; i ; i = Ed[i].upEd)
        if(Ed[i].end != p)
            if(!dep[Ed[i].end]){
                dis[Ed[i].end] = dis[x] + Ed[i].w;
                PII t = dfs1(Ed[i].end , x);
                if(t.st)
                    if(t.st == x){
                        ch[x].push_back(t.nd);
                        jump[t.nd][0][0] = x;
                    }
                    else{
                        ch[t.nd].push_back(x);
                        jump[x][0][0] = t.nd;
                        jump[x][0][1] = min(dis[x] - dis[t.st] , cir[t.nd] + dis[t.st] - dis[x]);
                        cur = t;
                    }
                else{
                    ch[x].push_back(Ed[i].end);
                    jump[Ed[i].end][0][0] = x;
                    jump[Ed[i].end][0][1] = Ed[i].w;
                }
            }
            else
                if(dep[Ed[i].end] < dep[x]){
                    cur.st = Ed[i].end;
                    cur.nd = ++cnt;
                    cir[cnt] = dis[x] - dis[Ed[i].end] + Ed[i].w;
                    ch[cnt].push_back(x);
                    jump[x][0][0] = cnt;
                    jump[x][0][1] = min(Ed[i].w , cir[cnt] - Ed[i].w);
                }
    return cur;
}

void dfs2(int x){
    for(int i = 1 ; jump[x][i - 1][0] ; ++i){
        jump[x][i][0] = jump[jump[x][i - 1][0]][i - 1][0];
        jump[x][i][1] = jump[x][i - 1][1] + jump[jump[x][i - 1][0]][i - 1][1];
    }
    for(int i = 0 ; i < ch[x].size() ; ++i){
        dep[ch[x][i]] = dep[x] + 1;
        dfs2(ch[x][i]);
    }
}

inline int abss(int a){
    return a < 0 ? -a : a;
}

int query(int x , int y){
    int sum = 0;
    if(dep[x] < dep[y])
        swap(x , y);
    for(int i = 15 ; i >= 0 ; --i)
        if(dep[x] - (1 << i) >= dep[y]){
            sum += jump[x][i][1];
            x = jump[x][i][0];
        }
    if(x == y)
        return sum;
    for(int i = 15 ; i >= 0 ; --i)
        if(jump[x][i][0] != jump[y][i][0]){
            sum = sum + jump[x][i][1] + jump[y][i][1];
            x = jump[x][i][0];
            y = jump[y][i][0];
        }
    if(jump[x][0][0] <= N)
        return sum + jump[x][0][1] + jump[y][0][1];
    else{
        bool f = 0;
        int p = jump[x][0][0] , t = jump[x][1][0];
        if(dis[x] - dis[t] > cir[p] + dis[t] - dis[x])
            f ^= 1;
        if(dis[y] - dis[t] > cir[p] + dis[t] - dis[y])
            f ^= 1;
        int l = f ? jump[x][0][1] + jump[y][0][1] : abss(jump[x][0][1] - jump[y][0][1]);
        return sum + min(l , cir[p] - l);
    }
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("in","r",stdin);
    freopen("out","w",stdout);
#endif
    N = cnt = read();
    M = read();
    Q = read();
    for(int i = 1 ; i <= M ; ++i){
        int a = read() , b = read() , c = read();
        addEd(a , b , c);
        addEd(b , a , c);
    }
    dfs1(1 , 0);
    dep[1] = 1;
    dfs2(1);
    for(int i = 1 ; i <= Q ; ++i)
        printf("%d\n" , query(read() , read()));
    return 0;
}

BZOJ2125 最短路 圆方树、倍增的更多相关文章

  1. [BZOJ2125]最短路(圆方树DP)

    题意:仙人掌图最短路. 算法:圆方树DP,$O(n\log n+Q\log n)$ 首先建出仙人掌圆方树(与点双圆方树的区别在于直接连割边,也就是存在圆圆边),然后考虑点u-v的最短路径,显然就是:在 ...

  2. [BZOJ2125]最短路[圆方树]

    题意 给定仙人掌,多次询问两点之间的最短路径. \(n\le 10000, Q\le 10000​\) 分析 建出圆方树,分路径 lca 是圆点还是方点讨论. 预处理出根圆点到每个圆点的最短距离 \( ...

  3. 2018.07.25 bzoj2125: 最短路(圆方树+倍增)

    传送门 人生的第一道仙人掌. 这道题求是仙人掌上的最短路. 先建出圆方树,然后用倍增跑最短路,当lca" role="presentation" style=" ...

  4. 【BZOJ】2125: 最短路 圆方树(静态仙人掌)

    [题意]给定带边权仙人掌图,Q次询问两点间最短距离.n,m,Q<=10000 [算法]圆方树处理仙人掌问题 [题解]树上的两点间最短路问题,常用倍增求LCA解决,考虑扩展到仙人掌图. 先对仙人掌 ...

  5. 仙人掌&圆方树学习笔记

    仙人掌&圆方树学习笔记 1.仙人掌 圆方树用来干啥? --处理仙人掌的问题. 仙人掌是啥? (图片来自于\(BZOJ1023\)) --也就是任意一条边只会出现在一个环里面. 当然,如果你的图 ...

  6. 【BZOJ2125】最短路(仙人掌,圆方树)

    [BZOJ2125]最短路(仙人掌,圆方树) 题面 BZOJ 求仙人掌上两点间的最短路 题解 终于要构建圆方树啦 首先构建出圆方树,因为是仙人掌,和一般图可以稍微的不一样 直接\(tarjan\)缩点 ...

  7. BZOJ.2125.最短路(仙人掌 圆方树)

    题目链接 圆方树.做题思路不写了.. 就是当LCA是方点时跳进那个环可以分类讨论一下用树剖而不必须用倍增: 如果v是u的(唯一的那个)重儿子,那么u的DFS序上+1的点即是要找的:否则v会引出一条新的 ...

  8. bzoj 2125 最短路 点双 圆方树

    LINK:最短路 一张仙人掌图 求图中两点最短路. \(n<=10000,Q<=10000,w>=1\) 考虑边数是多少 m>=n-1 对于一张仙人掌图 考虑先构建出来dfs树 ...

  9. 图论杂项细节梳理&模板(虚树,圆方树,仙人掌,欧拉路径,还有。。。)

    orzYCB 虚树 %自为风月马前卒巨佬% 用于优化一类树形DP问题. 当状态转移只和树中的某些关键点有关的时候,我们把这些点和它们两两之间的LCA弄出来,以点的祖孙关系连成一棵新的树,这就是虚树. ...

随机推荐

  1. 【读书笔记】iOS-分类与协议

    分类与协议是Object-C特有概念,分类(Category)可以认为是一种继承性的扩展,而协议(Protocol)可以理解为Java中的Interface(接口)或者C++的纯虚类. 参考资料:&l ...

  2. css 实用代码汇总

    1.table 排版(防止td文字过多导致table变形) table { /*为表格设置合并边框模型*/ border-collapse: collapse; border-spacing: 0; ...

  3. IDEA项目搭建七——使用Feign简化消费者端操作

    一.简介 我们可以看到上一篇文章的消费者这边调用Service时比较麻烦,所以我们可以使用Feign来简化这部分操作,它底层也是使用Ribbon实现的只是Ribbon支持HTTP和TCP两种通信协议, ...

  4. 工程设计文档服务EngineerCMS

    工程设计单位或个人的设计文件分类有其特点,利用engineercms的分类目录可以很好地管理资料.多单位,多人,多工程都可以适应. 其他engineercms是一个通用的文档管理,文档协作,在线预览d ...

  5. Appium学习——Appium工作原理

    appium的工具原理 Appium-client>>>>Appium-server>>>>移动设备 ========================= ...

  6. WebAPi使用Autofac实现依赖注入

    WebAPi依赖注入  使用记录 笔记 1.NuGet包安装 2.控制器加入构造函数 3.Global.asax  ----Application_Start 应用程序启动时 using Autofa ...

  7. HTTP协议响应码及get请求和post请求比较

    HTTP协议响应码 1XX:信息响应类,表示接收到请求并且继续处理 2XX:处理成功响应类,表示动作被成功接受.理解和接受 200 OK:表示从客户端发来的请求在服务器端被正常处理了 204 No C ...

  8. Web服务并发I/O模型

    I/O模型: 阻塞型.非阻塞型.复用型.信号驱动型.异步 同步/异步: 关注消息通知机制 消息通知: 同步:等待对方返回消息 异步:被调用者通过状态.通知或回调机制通知调用者被调用者的运行状态 阻塞/ ...

  9. 简单Nginx下防跨站、跨目录安全设置,支持PHP 5.3.3以上版本

    Nginx下存在跨站和跨目录的问题,跨站和跨目录影响同服务器/VPS上的其他网站. PHP在5.3.3以上已经增加了HOST配置,可以起到防跨站.跨目录的问题. 如果你是PHP 5.3.3以上的版本, ...

  10. luogu P4515 [COCI2009-2010#6] XOR

    luogu P4515 [COCI2009-2010#6] XOR 描述 坐标系下有若干个等腰直角三角形,且每个等腰直角三角形的直角顶点都在左下方,两腰与坐标轴平行.被奇数个三角形覆盖的面 积部分为灰 ...