传送门


注意到我们需要求的是两点之间所有简单路径中最小值的最小值,那么对于一个点双联通分量来说,如果要经过它,则一定会经过这个点双联通分量里权值最小的点

注意:这里不能缩边双联通分量,样例\(2\)就是一个反例

上面这个图如果缩点双会缩成\(3\)个,但是缩边双会将整个图缩成\(1\)个点。

假如我们询问的是\((1,4)\)之间的简单路径,而图中权值最小的点为\(7\)号点,那么如果缩成了边双联通分量,你的答案会是\(7\)号点的权值,意即认为可以走到\(7\)号点,但实际上如果到\(7\)号点,意味着\(5\)号点需要经过\(2\)次,不符合简单路径的要求

所以如果将题意改成“一条边只能经过一次”就是缩边双了

那么我们直接维护圆方树,对于每一个方点使用\(multiset\)维护与它相连的所有圆点的权值,在圆方树上树链剖分计算答案。

当然这样子还是不够的。考虑一种情况:一个圆点连接了一堆方点,然后在这一个圆点上不断进行修改操作,这样每一次修改都会波及一大堆方点的修改,复杂度直接爆炸。

优化:对于所有方点,不去维护它在圆方树上的父亲,那么对于每一次修改,只会波及它在圆方树上的方点父亲。而如果在某一次询问中两点之间的\(LCA\)为方点,还需要额外考虑这个点的父亲的贡献。

#include<bits/stdc++.h>
#define lch (x << 1)
#define rch (x << 1 | 1)
#define mid ((l + r) >> 1)
#define INF 0x7fffffff
//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 = 2e5 + 7;
struct Edge{
    int end , upEd;
}Ed[MAXN << 1];
int head[MAXN] , val[MAXN] , N , M , Q , cntEd , cnt;
int topS , ts , st[MAXN] , dfn[MAXN] , low[MAXN];
int ind[MAXN] , rk[MAXN] , fa[MAXN] , dep[MAXN] , son[MAXN] , sz[MAXN] , top[MAXN] , TS;
int Tree[MAXN << 2];
bool vis[MAXN];
vector < int > ch[MAXN];
multiset < int > s[MAXN];

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

inline void pop(int t , int bot){
    ch[t].push_back(++cnt);
    do{
        ch[cnt].push_back(st[topS]);
        s[cnt].insert(val[st[topS]]);
    }while(st[topS--] != bot);
}

void tarjan(int x , int p){
    st[++topS] = x;
    dfn[x] = low[x] = ++ts;
    vis[x] = 1;
    for(int i = head[x] ; i ; i = Ed[i].upEd)
        if(Ed[i].end != p)
            if(!vis[Ed[i].end]){
                tarjan(Ed[i].end , x);
                low[x] = min(low[x] , low[Ed[i].end]);
                if(low[Ed[i].end] >= dfn[x])
                    pop(x , Ed[i].end);
            }
            else
                low[x] = min(low[x] , dfn[Ed[i].end]);
}

void dfs1(int x , int p){
    fa[x] = p;
    dep[x] = dep[p] + 1;
    sz[x] = 1;
    for(int i = 0 ; i < ch[x].size() ; ++i){
        dfs1(ch[x][i] , x);
        sz[x] += sz[ch[x][i]];
        if(sz[son[x]] < sz[ch[x][i]])
            son[x] = ch[x][i];
    }
}

void dfs2(int x , int t){
    ind[x] = ++TS;
    rk[TS] = x;
    top[x] = t;
    if(!son[x])
        return;
    dfs2(son[x] , t);
    for(int i = 0 ; i < ch[x].size() ; ++i)
        if(ch[x][i] != son[x])
            dfs2(ch[x][i] , ch[x][i]);
}

inline void pushup(int x){
    Tree[x] = min(Tree[lch] , Tree[rch]);
}

void init(int x , int l , int r){
    if(l == r)
        Tree[x] = rk[l] <= N ? val[rk[l]] : *s[rk[l]].begin();
    else{
        init(lch , l , mid);
        init(rch , mid + 1 , r);
        pushup(x);
    }
}

void modify(int x , int l , int r , int tar , int num){
    if(l == r)
        Tree[x] = num;
    else{
        if(mid >= tar)
            modify(lch , l , mid , tar , num);
        else
            modify(rch , mid + 1 , r , tar , num);
        pushup(x);
    }
}

int query(int x , int l , int r , int L , int R){
    if(l >= L && r <= R)
        return Tree[x];
    int minN = INF;
    if(mid >= L)
        minN = min(minN , query(lch , l , mid , L , R));
    if(mid < R)
        minN = min(minN , query(rch , mid + 1 , r , L , R));
    return minN;
}

int work(int x , int y){
    int tx = top[x] , ty = top[y] , minN = INF;
    while(tx != ty){
        if(dep[tx] < dep[ty]){
            swap(x , y);
            swap(tx , ty);
        }
        minN = min(minN , query(1 , 1 , cnt , ind[tx] , ind[x]));
        x = fa[tx];
        tx = top[x];
    }
    if(dep[x] > dep[y])
        swap(x , y);
    minN = min(minN , query(1 , 1 , cnt , ind[x] , ind[y]));
    if(x > N)
        minN = min(minN , val[fa[x]]);
    return minN;
}

inline char getc(){
    char c = getchar();
    while(!isupper(c))
        c = getchar();
    return c;
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("in","r",stdin);
    //freopen("out","w",stdout);
#endif
    cnt = N = read();
    M = read();
    Q = read();
    for(int i = 1 ; i <= N ; ++i)
        val[i] = read();
    for(int i = 1 ; i <= M ; ++i){
        int a = read() , b = read();
        addEd(a , b);
        addEd(b , a);
    }
    tarjan(1 , 0);
    dfs1(1 , 0);
    dfs2(1 , 1);
    init(1 , 1 , cnt);
    while(Q--)
        if(getc() == 'A')
            printf("%d\n" , work(read() , read()));
        else{
            int a = read() , b = read();
            if(a != 1){
                s[fa[a]].erase(s[fa[a]].find(val[a]));
                s[fa[a]].insert(b);
                modify(1 , 1 , cnt , ind[fa[a]] , *s[fa[a]].begin());
            }
            val[a] = b;
            modify(1 , 1 , cnt , ind[a] , b);
        }
    return 0;
}

CF487E Tourists 圆方树、树链剖分的更多相关文章

  1. CF487E Tourists(圆方树+树链剖分+multiset/可删堆)

    CF487E Tourists(圆方树+树链剖分+multiset/可删堆) Luogu 给出一个带点权的无向图,两种操作: 1.修改某点点权. 2.询问x到y之间简单路径能走过的点的最小点权. 题解 ...

  2. CF487E Tourists + 圆方树学习笔记(圆方树+树剖+线段树+multiset)

    QWQ果然我已经什么都学不会的人了. 这个题目要求的是图上所有路径的点权和!QWQ(我只会树上啊!) 这个如果是好啊 这时候就需要 圆方树! 首先在介绍圆方树之前,我们先来一点简单的前置知识 首先,我 ...

  3. CF487E Tourists[圆方树+树剖(线段树套set)]

    做这题的时候有点怂..基本已经想到正解了..结果感觉做法有点假,还是看了正解题解.. 首先提到简单路径上经过的点,就想到了一个关于点双的结论:两点间简单路径上所有可能经过的点的并等于路径上所有点所在点 ...

  4. uoj30【CF Round #278】Tourists(圆方树+树链剖分+可删除堆)

    - 学习了一波圆方树 学习了一波点分治 学习了一波可删除堆(巧用 ? STL) 传送门: Icefox_zhx 注意看代码看怎么构建圆方树的. tips:tips:tips:圆方树内存记得开两倍 CO ...

  5. Tourists——圆方树

    CF487E Tourists 一般图,带修求所有简单路径代价. 简单路径,不能经过同一个点两次,那么每个V-DCC出去就不能再回来了. 所以可以圆方树,然后方点维护一下V-DCC内的最小值. 那么, ...

  6. 线段树&数链剖分

    傻逼线段树,傻逼数剖 线段树 定义: 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点. 使用线段树可以快速的查找某一个节点在若干条线段中出现 ...

  7. CF487E Tourists - Tarjan缩点 + 树剖 + multiset

    Solution 先Tarjan求出点双联通分量 并缩点. 用$multiset$维护 点双内的最小点权. 容易发现, 点双内的最小点权必须包括与它相连的割边的点权. 所以我们必须想办法来维护. 所以 ...

  8. UOJ#30/Codeforces 487E Tourists 点双连通分量,Tarjan,圆方树,树链剖分,线段树

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ30.html 题目传送门 - UOJ#30 题意 uoj写的很简洁.清晰,这里就不抄一遍了. 题解 首先建 ...

  9. CF487E Tourists 【圆方树 + 树剖 + 堆】

    题目链接 CF487E 题解 圆方树 + 树剖 裸题 建好圆方树维护路径上最小值即可 方点的值为其儿子的最小值,这个用堆维护 为什么只维护儿子?因为这样修改点的时候就只需要修改其父亲的堆 这样充分利用 ...

随机推荐

  1. Fiddler抓包使用教程-过滤

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/72929800 本文出自[赵彦军的博客] Fiddler抓包可以完成我们移动开发者的 ...

  2. 使用过AsyncTask、EventBus、Volley以及Retrofit,必须好好了解handler运行机制

    我们都知道在UI线程中不能进行耗时操作,例如数据读写.网络请求.Android 4.0开始,在主线程中进行网络请求甚至会抛出Android.os.NetworkOnMainThreadExceptio ...

  3. (网页)AngularJS 参考手册

    指令 描述 ng-app 定义应用程序的根元素. ng-bind 绑定 HTML 元素到应用程序数据 ng-bind-html 绑定 HTML 元素的 innerHTML 到应用程序数据,并移除 HT ...

  4. 9.4、__del__、__doc__、__dict__、__module__、__getitem__、__setitem__、__delitem__、__str__、__repr__、__call__

    相关内容: __del__.__doc__.__dict__.__module__.__getitem__.__setitem__.__delitem__.__str__.__repr__.__cal ...

  5. JavaScript大杂烩0 - WEB基础知识

    1. 协议小结:HTTP协议与TCP/IP协议 现代Web应用开发的基础是HTTP协议,那么HTTP协议与我们熟知的TCP/IP协议有什么关系呢? 这个要从网络通信模型说起,简单的说,计算机通信就像两 ...

  6. Postgre SQL连接服务器失败

    首先这是登陆postgre sql时提示的错误信息: psql: 无法联接到服务器: Connection refused (0x0000274D/10061)        服务器是否在主机 &qu ...

  7. MySQL--eq_range_index_dive_limit参数学习,MYSQL 5.6 5.7处理数据分布不均的问题

    官方文档如下描述:This variable indicates the number of equality ranges in an equality comparison condition w ...

  8. 用Python实现数据结构之树

    树 树是由根结点和若干颗子树构成的.树是由一个集合以及在该集合上定义的一种关系构成的.集合中的元素称为树的结点,所定义的关系称为父子关系.父子关系在树的结点之间建立了一个层次结构.在这种层次结构中有一 ...

  9. Python交互模式下代码自动补全

    这个功能是以lib的形式提供的,配置写到home下的.pythonrc文件中, 并设置好环境变量让python启动时执行初始化: # ~/.pythonrc # enable syntax compl ...

  10. Linux 小知识翻译 - 「Linux」和病毒

    据说,「Linux」系统上的病毒要远远少于Windows系统上病毒.从2种系统的普及度来看,这是很显然的, 「Linux」的使用人群很少,所以「Linux」上的病毒的扩散时,受害的范围也不大. 但是, ...