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

题目链接

CF487E

题解

圆方树 + 树剖 裸题

建好圆方树维护路径上最小值即可

方点的值为其儿子的最小值,这个用堆维护

为什么只维护儿子?因为这样修改点的时候就只需要修改其父亲的堆

这样充分利用了一对一的特性优化了复杂度

如此询问时如果\(lca\)为方点,再询问一下\(lca\)的父亲即可

复杂度\(O(qlog^2n)\)

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include<cmath>
#include<vector>
#include<map>
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define mp(a,b) make_pair<int,int>(a,b)
#define cls(s) memset(s,0,sizeof(s))
#define cp pair<int,int>
#define LL long long int
#define ls (u << 1)
#define rs (u << 1 | 1)
using namespace std;
const int maxn = 200005,maxm = 100005,INF = 1000000000;
inline int read(){
    int out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57){if (c == ‘-‘) flag = -1; c = getchar();}
    while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    return out * flag;
}
int hh[maxn],nne = 1,h[maxn],ne = 1;
struct EDGE{int to,nxt;}ed[maxn << 1],e[maxn << 1];
inline void build(int u,int v){
    e[++nne] = (EDGE){v,hh[u]}; hh[u] = nne;
    e[++nne] = (EDGE){u,hh[v]}; hh[v] = nne;
}
inline void add(int u,int v){
    ed[++ne] = (EDGE){v,h[u]}; h[u] = ne;
    ed[++ne] = (EDGE){u,h[v]}; h[v] = ne;
}
struct HEAP{
    priority_queue<int,vector<int>,greater<int> > a,b;
    void ck(){while (!b.empty() && a.top() == b.top()) a.pop(),b.pop();}
    int size(){return a.size() - b.size();}
    void ins(int x){a.push(x);}
    void del(int x){b.push(x);}
    int top(){ck(); return size() ? a.top() : INF;}
}H[maxn];
int n,m,q,N,w[maxn];
int dfn[maxn],low[maxn],st[maxn],Top,cnt;
void dfs(int u,int las){
    dfn[u] = low[u] = ++cnt; st[++Top] = u;
    for (int k = hh[u],to; k; k = e[k].nxt)
        if (k != las){
            if (!dfn[to = e[k].to]){
                dfs(to,k ^ 1);
                low[u] = min(low[u],low[to]);
                if (low[to] >= dfn[u]){
                    add(++N,u);
                    do{add(N,st[Top]);} while (st[Top--] != to);
                }
            }
            else low[u] = min(low[u],dfn[to]);
        }
}
int siz[maxn],top[maxn],dep[maxn],fa[maxn],son[maxn],id[maxn],Hash[maxn],Cnt;
void dfs1(int u){
    siz[u] = 1;
    Redge(u) if ((to = ed[k].to) != fa[u]){
        fa[to] = u; dep[to] = dep[u] + 1;
        dfs1(to);
        if (u > n) H[u - n].ins(w[to]);
        siz[u] += siz[to];
        if (!son[u] || siz[to] > siz[son[u]]) son[u] = to;
    }
    if (u > n) w[u] = H[u - n].top();
}
void dfs2(int u,int flag){
    top[u] = flag ? top[fa[u]] : u;
    id[u] = ++Cnt; Hash[Cnt] = u;
    if (son[u]) dfs2(son[u],1);
    Redge(u) if ((to = ed[k].to) != fa[u] && to != son[u])
        dfs2(to,0);
}
int mn[maxn << 2];
inline void upd(int u){mn[u] = min(mn[ls],mn[rs]);}
void build(int u,int l,int r){
    if (l == r){
        mn[u] = w[Hash[l]];
        return;
    }
    int mid = l + r >> 1;
    build(ls,l,mid);
    build(rs,mid + 1,r);
    upd(u);
}
void modify(int u,int l,int r,int pos,int v){
    if (l == r){mn[u] = v; return;}
    int mid = l + r >> 1;
    if (mid >= pos) modify(ls,l,mid,pos,v);
    else modify(rs,mid + 1,r,pos,v);
    upd(u);
}
int query(int u,int l,int r,int L,int R){
    if (l >= L && r <= R) return mn[u];
    int mid = l + r >> 1;
    if (mid >= R) return query(ls,l,mid,L,R);
    if (mid < L) return query(rs,mid + 1,r,L,R);
    return min(query(ls,l,mid,L,R),query(rs,mid + 1,r,L,R));
}
int solve1(int u,int v){
    int ans = INF;
    while (top[u] != top[v]){
        if (dep[top[u]] < dep[top[v]]) swap(u,v);
        ans = min(ans,query(1,1,N,id[top[u]],id[u]));
        u = fa[top[u]];
    }
    if (dep[u] > dep[v]) swap(u,v);
    ans = min(ans,query(1,1,N,id[u],id[v]));
    if (u > n && fa[u]) ans = min(ans,w[fa[u]]);
    return ans;
}
void solve2(int u,int v){
    modify(1,1,N,id[u],v);
    if (fa[u]){
        H[fa[u] - n].del(w[u]),H[fa[u] - n].ins(v);
        w[fa[u]] = H[fa[u] - n].top();
        modify(1,1,N,id[fa[u]],w[fa[u]]);
    }
    w[u] = v;
}
int main(){
    N = n = read();  m = read(); q = read();
    for (int i = 1; i <= n; i++) w[i] = read();
    for (int i = 1; i <= m; i++) build(read(),read());
    dfs(1,0);
    dfs1(1);
    dfs2(1,0);
    build(1,1,N);
    char opt; int a,b;
    while (q--){
        opt = getchar(); while (opt != ‘A‘ && opt != ‘C‘) opt = getchar();
        a = read(); b = read();
        if (opt == ‘A‘) printf("%d\n",solve1(a,b));
        else solve2(a,b);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Mychael/p/9203037.html

时间: 2024-10-09 18:21:44

CF487E Tourists 【圆方树 + 树剖 + 堆】的相关文章

CF487E Tourists 圆方树、树链剖分

传送门 注意到我们需要求的是两点之间所有简单路径中最小值的最小值,那么对于一个点双联通分量来说,如果要经过它,则一定会经过这个点双联通分量里权值最小的点 注意:这里不能缩边双联通分量,样例\(2\)就是一个反例 上面这个图如果缩点双会缩成\(3\)个,但是缩边双会将整个图缩成\(1\)个点. 假如我们询问的是\((1,4)\)之间的简单路径,而图中权值最小的点为\(7\)号点,那么如果缩成了边双联通分量,你的答案会是\(7\)号点的权值,意即认为可以走到\(7\)号点,但实际上如果到\(7\)号

Tourists——圆方树

CF487E Tourists 一般图,带修求所有简单路径代价. 简单路径,不能经过同一个点两次,那么每个V-DCC出去就不能再回来了. 所以可以圆方树,然后方点维护一下V-DCC内的最小值. 那么,从任意一个割点进入这个DCC,必然可以绕一圈再从另一个割点出去. 所以,路径上的最小值,就是圆方树路径上的最小值.方点的最小值就是在这个DCC中走一走得到的. 树链剖分+线段树维护路径 用堆维护方点四周的圆点的最小值.然后更新. 一个问题是: 更新一个割点圆点,会影响到四周所有的方点.暴力更新,菊花

CF487E Tourists (圆方树,LCT)

圆方树模板题. 建出圆方树. 对于每个方点,只维护方点儿子的最小值,不维护方点父亲的值,这样的话每次修改只会改一个方点. 我们需要支持单点修改,链查询,求 lca. LCT 可以非常方便地维护这些东西,然后如果 lca 是方点的话特判一下方点父亲的点值即可. code: #include <cstdio> #include <algorithm> #include <set> #include <vector> #include <cstring>

[SDOI2018]战略游戏 圆方树,树链剖分

[SDOI2018]战略游戏 这题是道路相遇(题解)的升级版,询问的两个点变成了\(S\)个点. LG传送门 还是先建出圆方树,考虑对于询问的\(S\)个点,答案就是圆方树上能包含这些点的最小连通块中的圆点个数减去\(S\).问题变成了怎样求这样的连通块中的圆点个数,直接给结论吧:先搞出树的dfs序,把询问的点按dfs序从小到大排一遍序,每次把答案加上第\(i\)和第\(i + 1\)个点之间的圆点个数,但是不算lca,再加上第\(1\)个和第\(S\)个点之间的圆点个数,然后除以二就得到了这个

圆方树总结

圆方树:一种将由图转化而成的树,从而大大了增加题目的可解性,且大多广泛用于仙人掌图中. 针对仙人掌图上的圆方树:仙人掌是指一条边至多只被一个环包含的无向图. 树上的点:圆方树上分为两类点,一类是圆点,一类是方点.圆点即原图中所有的点,方点即为了去环而新添加进去的,满足一定性质的点. 构造思路:圆圆边直接加入,对于仙人掌中的任意一个环,每个环上的点在圆方树上对应的圆点向这个环对应的方点连边,方点为一个新建节点. 环的根:指定一个圆点为圆方树的根,把方点的父亲叫做这个方点对应的环的根. 圆方边边权:

圆方树学习

圆方树是一种数据结构. 这个东西原始的出处应该是paper <Maintaining bridge-connected and biconnected components on-line> tarjan和另外一个人写的...当时叫forest data structure 然后这个东西似乎已经流行很久了?http://blog.csdn.net/PoPoQQQ/article/details/49513819 cjk大爷最近发了一篇博客写这个:http://immortalco.blog.u

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

[题意]给定带边权仙人掌图,Q次询问两点间最短距离.n,m,Q<=10000 [算法]圆方树处理仙人掌问题 [题解]树上的两点间最短路问题,常用倍增求LCA解决,考虑扩展到仙人掌图. 先对仙人掌图建圆方树,圆圆边和原图边权一致.对于每个方点代表的环,记深度最小的点为x,则圆方边的边权是圆点到x的最短距离. 若lca(u,v)为圆点,则两点间最短路转化为圆方树上dis[u]+dis[v]-2*dis[lca].(向上延伸的路径,经过环则必然经过每个方点的x,计算无误) 若lca(u,v)为方点,则

【P4320】 道路相遇 (圆方树+LCA)

题目链接 题意:给一张无向图和\(M\)个询问,问\(u,v\)之间的路径的必经之点的个数. 对图建出圆方树,然后必经之点就是两点路径经过的原点个数,用\((dep[u]+dep[v]-dep[LCA]*2)/2+1\)即可算出. 什么你不知道圆方树(说的跟我知道一样) \(APIO2018\)出来的黑科技,详见\(APIO2018\)铁人两项. 就是对每个点双新建一个点,然后让点双里所有点都对这个点连边. 看图. #include <cstdio> const int MAXN = 5000

道路相遇 圆方树

道路相遇 基础圆方树. 不会圆方树看我另一篇文章 LG传送门 发现必经之路上的点一定是简单路径上的点,可以自己手玩.处理无向图上的简单路径,考虑把圆方树建出来,发现答案就是园方树上两点间圆点个数.由于广义园方树上圆方点相间,可以用深度表示答案,发现答案就是\((dep[u] + dep[v] - 2 * dep[lca]) / 2 + 1\). #include <cstdio> #include <cctype> #include <vector> #define R