CF487E Tourists (圆方树,LCT)

圆方树模板题.

建出圆方树.

对于每个方点,只维护方点儿子的最小值,不维护方点父亲的值,这样的话每次修改只会改一个方点.

我们需要支持单点修改,链查询,求 lca.

LCT 可以非常方便地维护这些东西,然后如果 lca 是方点的话特判一下方点父亲的点值即可.

code:

#include <cstdio>
#include <algorithm>
#include <set>
#include <vector>
#include <cstring>  

#define setI(s) freopen(s".in","r",stdin)
#define setO(s) freopen(s".out","w",stdout)
#define setIO(s) setI(s),setO(s)  

using namespace std;   

const int N=2e5+5;
const int inf=1e9+2;                     

namespace lct {
    #define lson s[x].ch[0]
    #define rson s[x].ch[1]
    struct data
    {
        int ch[2],rev,val,mi,f;
        data(int x=inf) { ch[0]=ch[1]=0,val=mi=x; }
    }s[N];
    int sta[N];
    int get(int x) { return s[s[x].f].ch[1]==x; }
    int isr(int x) { return s[s[x].f].ch[0]!=x&&s[s[x].f].ch[1]!=x; }   

    void pushup(int x)
    {
        s[x].mi=min(s[x].val,min(s[lson].mi,s[rson].mi));
    }
    void mark(int x)
    {
        if(!x) return;
        swap(lson,rson),s[x].rev^=1;
    }
    void pushdown(int x)
    {
        if(s[x].rev)
            mark(lson),mark(rson),s[x].rev=0;
    }
    void rotate(int x)
    {
        int old=s[x].f,fold=s[old].f,which=get(x);
        if(!isr(old))
            s[fold].ch[s[fold].ch[1]==old]=x;
        s[old].ch[which]=s[x].ch[which^1];
        if(s[old].ch[which])
            s[s[old].ch[which]].f=old;
        s[x].ch[which^1]=old,s[old].f=x,s[x].f=fold;
        pushup(old),pushup(x);
    }
    void splay(int x)
    {
        int u=x,v=0,fa;
        for(sta[++v]=u;!isr(u);u=s[u].f)
            sta[++v]=s[u].f;
        for(;v;--v)
            pushdown(sta[v]);
        for(u=s[u].f;(fa=s[x].f)!=u;rotate(x))
            if(s[fa].f!=u)
                rotate(get(fa)==get(x)?fa:x);
    }
    int Access(int x)
    {
        int y=0;
        for(;x;y=x,x=s[x].f)
            splay(x),rson=y,pushup(x);
        return y;
    }
    void makert(int x)
    {
        Access(x),splay(x),mark(x);
    }
    void split(int x,int y)
    {
        makert(x),Access(y),splay(y);
    }
    void link(int x,int y)
    {
        s[x].f=y;
    }
    int get_lca(int x,int y)
    {
        Access(x);
        return Access(y);
    }
};    

vector<int>G[N];
int val[N],dfn[N],low[N],S[N],tim,top,tot;
int hd[N],to[N],nex[N],edges,n,m,Q;
int fa[N];
multiset<int>mi[N];
void add(int u,int v)
{
    nex[++edges]=hd[u],hd[u]=edges,to[edges]=v;
}
void tarjan(int u)
{
    dfn[u]=low[u]=++tim;
    S[++top]=u;
    for(int i=hd[u];i;i=nex[i])
    {
        int v=to[i];
        if(!dfn[v])
        {
            tarjan(v),low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u])
            {
                ++tot;
                G[tot].push_back(u);
                G[u].push_back(tot);
                for(int x=0;x!=v;--top)
                {
                    x=S[top];
                    G[tot].push_back(x);
                    G[x].push_back(tot);
                }
            }
        }
        else low[u]=min(low[u],dfn[v]);
    }
}
void dfs(int x,int ff)
{
    if(ff)
        lct::link(x,ff);
    if(x<=n)
    {
        if(ff) mi[ff].insert(val[x]);
        lct::s[x].val=lct::s[x].mi=val[x];
    }
    for(int i=0;i<G[x].size();++i)
    {
        int y=G[x][i];
        if(y!=ff) fa[y]=x,dfs(y,x);
    }
    if(x>n)
    {
        lct::s[x].val=lct::s[x].mi=*mi[x].begin();
    }
}
int main()
{
    //setI("input");
    scanf("%d%d%d",&n,&m,&Q);
    for(int i=1;i<=n;++i)
        scanf("%d",&val[i]);
    for(int i=1;i<=m;++i)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y),add(y,x);
    }
    tot=n;
    for(int i=1;i<=n;++i)
        if(!dfn[i])
            tarjan(i);
    dfs(1,0);
    val[0]=inf;
    for(int i=1;i<=Q;++i)
    {
        char ch[2];
        int x,b;
        scanf("%s%d%d",ch,&x,&b);
        if(ch[0]==‘C‘)
        {
            lct::splay(x);
            lct::s[x].val=b;
            lct::pushup(x);           

            if(fa[x])
            {
                mi[fa[x]].erase(mi[fa[x]].lower_bound(val[x]));
                mi[fa[x]].insert(b);
                lct::splay(fa[x]);
                lct::s[fa[x]].val=*mi[fa[x]].begin();
                lct::pushup(fa[x]);
            }              

            val[x]=b;
            // modify(val[x] -> b)
        }
        else
        {
            // query(a,b)
            int lca=lct::get_lca(x,b);
            if(lca<=n)  lca=0;
            lct::split(x,b);
            printf("%d\n",min(lct::s[b].mi,val[fa[lca]]));
            lct::makert(1);
        }
    }
    return 0;
}

  

原文地址:https://www.cnblogs.com/guangheli/p/12586841.html

时间: 2024-07-31 04:25:54

CF487E Tourists (圆方树,LCT)的相关文章

CF487E Tourists 圆方树、树链剖分

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

Tourists——圆方树

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

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

题目链接 CF487E 题解 圆方树 + 树剖 裸题 建好圆方树维护路径上最小值即可 方点的值为其儿子的最小值,这个用堆维护 为什么只维护儿子?因为这样修改点的时候就只需要修改其父亲的堆 这样充分利用了一对一的特性优化了复杂度 如此询问时如果\(lca\)为方点,再询问一下\(lca\)的父亲即可 复杂度\(O(qlog^2n)\) #include<algorithm> #include<iostream> #include<cstring> #include<

圆方树学习

圆方树是一种数据结构. 这个东西原始的出处应该是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

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

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

[圆方树] Luogu P4630 Duathlon 铁人两项

题目描述 比特镇的路网由 mm 条双向道路连接的 nn 个交叉路口组成. 最近,比特镇获得了一场铁人两项锦标赛的主办权.这场比赛共有两段赛程:选手先完成一段长跑赛程,然后骑自行车完成第二段赛程. 比赛的路线要按照如下方法规划: 先选择三个两两互不相同的路口 s, cs,c和 ff,分别作为比赛的起点.切换点(运动员在长跑到达这个点后,骑自行车前往终点).终点. 选择一条从 ss出发,经过 cc最终到达 ff的路径.考虑到安全因素,选择的路径经过同一个点至多一次. 在规划路径之前,镇长想请你帮忙计