【题解】Luogu SP8791 DYNALCA - Dynamic LCA

原题传送门

这题用Link-Cut-Tree解决,Link-Cut-Tree详解

这道题的难点就在如何求LCA:

我们珂以先对其中一个点进行access操作,然后对另一个点进行access操作,因为LCA到根的边一定都由第一次access变为实边了,在之后的这一次access操作的最后一条从虚边变为实边的边的父亲就是两点的LCA

回求LCA后,剩下的几乎是模板,但有两种做法

1.建立虚拟点n+1,保证一直是一颗有n+1个点的树,写起来比较无脑(lct部分珂以复制),常数比较大

#include <bits/stdc++.h>
#define N 100005
#define getchar nc
using namespace std;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
    register int x=0,f=1;register char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return x*f;
}
inline void write(register int x)
{
    if(!x)putchar('0');if(x<0)x=-x,putchar('-');
    static int sta[20];register int tot=0;
    while(x)sta[tot++]=x%10,x/=10;
    while(tot)putchar(sta[--tot]+48);
}
inline void Swap(register int &a,register int &b)
{
    a^=b^=a^=b;
}
struct Link_Cut_Tree{
    int c[N][2],fa[N],top,q[N],rev[N];
    inline void pushdown(register int x)
    {
        if(rev[x])
        {
            register int l=c[x][0],r=c[x][1];
            rev[l]^=1,rev[r]^=1,rev[x]^=1;
            Swap(c[x][0],c[x][1]);
        }
    }
    inline bool isroot(register int x)
    {
        return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;
    }
    inline void rotate(register int x)
    {
        int y=fa[x],z=fa[y],l,r;
        l=c[y][0]==x?0:1;
        r=l^1;
        if(!isroot(y))
            c[z][c[z][0]==y?0:1]=x;
        fa[x]=z;
        fa[y]=x;
        fa[c[x][r]]=y;
        c[y][l]=c[x][r];
        c[x][r]=y;
    }
    inline void splay(register int x)
    {
        top=1;
        q[top]=x;
        for(register int i=x;!isroot(i);i=fa[i])
            q[++top]=fa[i];
        for(register int i=top;i;--i)
            pushdown(q[i]);
        while(!isroot(x))
        {
            int y=fa[x],z=fa[y];
            if(!isroot(y))
                rotate((c[y][0]==x)^(c[z][0]==y)?(x):(y));
            rotate(x);
        }
    }
    inline void access(register int x)
    {
        for(register int t=0;x;t=x,x=fa[x])
        {
            splay(x);
            c[x][1]=t;
        }
    }
    inline void makeroot(register int x)
    {
        access(x);
        splay(x);
        rev[x]^=1;
    }
    inline void split(register int x,register int y)
    {
        makeroot(x);
        access(y);
        splay(y);
    }
    inline void cut(register int x,register int y)
    {
        split(x,y);
        c[y][0]=0;
        fa[x]=0;
    }
    inline void link(register int x,register int y)
    {
        makeroot(x);
        fa[x]=y;
    }
    inline int LCA(register int x,register int y)
    {
        access(x);
        int t;
        for(t=0;y;t=y,y=fa[y])
        {
            splay(y);
            c[y][1]=t;
        }
        return t;
    }
}T;
int fa[N];
int main()
{
    int n=read(),m=read();
    for(register int i=1;i<=n;++i)
        T.link(i,n+1),fa[i]=n+1;
    while(m--)
    {
        char c=getchar();
        while(c!='k'&&c!='t'&&c!='a')
            c=getchar();
        if(c=='k')
        {
            int x=read(),y=read();
            T.cut(x,fa[x]);
            fa[x]=y;
            T.link(x,y);
        }
        else if(c=='t')
        {
            int x=read();
            T.cut(x,fa[x]);
            fa[x]=n+1;
            T.link(x,n+1);
        }
        else
        {
            int x=read(),y=read();
            T.makeroot(n+1);
            write(T.LCA(x,y)),puts("");
        }
    }
    return 0;
 } 

2.按照题目加边,删边(常数小,容易写错)

注意:cut和LCA操作是不能使用makeroot操作的,因为makeroot本身会修改树上的LCA关系,当然link操作使用makeroot是没有关系的

#include <bits/stdc++.h>
#define N 100005
#define getchar nc
using namespace std;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
    register int x=0,f=1;register char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return x*f;
}
inline void write(register int x)
{
    if(!x)putchar('0');if(x<0)x=-x,putchar('-');
    static int sta[20];register int tot=0;
    while(x)sta[tot++]=x%10,x/=10;
    while(tot)putchar(sta[--tot]+48);
}
inline void Swap(register int &a,register int &b)
{
    a^=b^=a^=b;
}
struct Link_Cut_Tree{
    int c[N][2],fa[N],top,q[N],rev[N];
    inline void pushdown(register int x)
    {
        if(rev[x])
        {
            register int l=c[x][0],r=c[x][1];
            rev[l]^=1,rev[r]^=1,rev[x]^=1;
            Swap(c[x][0],c[x][1]);
        }
    }
    inline bool isroot(register int x)
    {
        return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;
    }
    inline void rotate(register int x)
    {
        int y=fa[x],z=fa[y],l,r;
        l=c[y][0]==x?0:1;
        r=l^1;
        if(!isroot(y))
            c[z][c[z][0]==y?0:1]=x;
        fa[x]=z;
        fa[y]=x;
        fa[c[x][r]]=y;
        c[y][l]=c[x][r];
        c[x][r]=y;
    }
    inline void splay(register int x)
    {
        top=1;
        q[top]=x;
        for(register int i=x;!isroot(i);i=fa[i])
            q[++top]=fa[i];
        for(register int i=top;i;--i)
            pushdown(q[i]);
        while(!isroot(x))
        {
            int y=fa[x],z=fa[y];
            if(!isroot(y))
                rotate((c[y][0]==x)^(c[z][0]==y)?(x):(y));
            rotate(x);
        }
    }
    inline void access(register int x)
    {
        for(register int t=0;x;t=x,x=fa[x])
        {
            splay(x);
            c[x][1]=t;
        }
    }
    inline void makeroot(register int x)
    {
        access(x);
        splay(x);
        rev[x]^=1;
    }
    inline void cut(register int x)
    {
        access(x);
        splay(x);
        fa[c[x][0]]=0;
        c[x][0]=0;
    }
    inline void link(register int x,register int y)
    {
        makeroot(x);
        fa[x]=y;
    }
    inline int LCA(register int x,register int y)
    {
        access(x);
        int t;
        for(t=0;y;t=y,y=fa[y])
        {
            splay(y);
            c[y][1]=t;
        }
        return t;
    }
}T;
int fa[N];
int main()
{
    int n=read(),m=read();
    while(m--)
    {
        char c=getchar();
        while(c!='k'&&c!='t'&&c!='a')
            c=getchar();
        if(c=='k')
        {
            int x=read(),y=read();
            T.link(x,y);
        }
        else if(c=='t')
        {
            int x=read();
            T.cut(x);
        }
        else
        {
            int x=read(),y=read();
            write(T.LCA(x,y)),puts("");
        }
    }
    return 0;
 } 

原文地址:https://www.cnblogs.com/yzhang-rp-inf/p/10204881.html

时间: 2024-11-02 20:17:57

【题解】Luogu SP8791 DYNALCA - Dynamic LCA的相关文章

spoj DYNALCA - Dynamic LCA

http://www.spoj.com/problems/DYNALCA/ 此题link.cut要求不能换根,当然也保证link时其中一个点必定已经是根. 方法: void link(Node *x,Node *y) { access(x);splay(x); x->fa=y; } void cut(Node *x) { access(x);splay(x); x->ch[0]->fa=NULL;x->ch[0]=NULL; } 曾经的错误思路: void cut(Node *x)

题解 luogu P1850 【换教室】

题解 luogu P1850 [换教室] 时间:2019.8.6 一晚上(约 3.5h 写完) 题目描述 对于刚上大学的牛牛来说,他面临的第一个问题是如何根据实际情况申请合适的课程. 在可以选择的课程中,有 \(2n\) 节课程安排在 \(n\) 个时间段上.在第 \(i\)(\(1 \leq i \leq n\))个时间段上,两节内容相同的课程同时在不同的地点进行,其中,牛牛预先被安排在教室 \(c_i\) 上课,而另一节课程在教室 \(d_i\) 进行. 在不提交任何申请的情况下,学生们需要

题解 luogu P5021 【赛道修建】

题解 luogu P5021 [赛道修建] 时间:2019.8.9 20:40 时间:2019.8.12 题目描述 C 城将要举办一系列的赛车比赛.在比赛前,需要在城内修建 \(m\) 条赛道. C 城一共有 \(n\) 个路口,这些路口编号为 \(1,2,\dots,n\),有 \(n-1\) 条适合于修建赛道的双向通行的道路,每条道路连接着两个路口.其中,第 \(i\) 条道路连接的两个路口编号为 \(a_i\) 和 \(b_i\),该道路的长度为 \(l_i\).借助这 \(n-1\) 条

题解 Luogu P2499: [SDOI2012]象棋

关于这道题, 我们可以发现移动顺序不会改变答案, 具体来说, 我们有以下引理成立: 对于一个移动过程中的任意一个移动, 若其到达的位置上有一个棋子, 则该方案要么不能将所有棋子移动到最终位置, 要么可以通过改变顺序使这一次移动合法 证明: 考虑到达位置上的那个棋子, 如果它没有到达最终位置, 则我们考虑将该棋子移至下一步, 如果下一步还有没有到达最终位置的棋子, 则也移动它 否则直接调换这两个棋子的移动顺序即可 好的我们去除了题目中的要求: 「移动过程中不能出现多颗棋子同时在某一格的情况」, 接

题解 Luogu P3370

讲讲这题的几种做法: 暴力匹配法 rt,暴力匹配,即把字符串存起来一位一位判相等 时间复杂度$ O(n^2·m) $ 再看看数据范围 $n\le10^5,m\le10^3$ 当场爆炸.当然有暴力分 代码(20pts): #include <bits/stdc++.h> using namespace std; char c[100001][1001]; bool pd(int x, int y) { int l1 = strlen(c[x]), l2 = strlen(c[y]); if(l1

「Luogu P2617」Dynamic Rankings

给出一段序列,每次修改某个数的值和询问区间第 k 小.\((1\le n,m\le 10^5,0\le a_i\le10^9)\) Luogu 分析 动态主席树裸题. 树状数组套主席树,树状数组的每个结点相当于一棵主席树,每次修改操作只在对应树状数组的 logn 个结点所对应的主席树上修改,查询时,将 l - 1 和 r 分别对应的 logn 棵主席树作差即可. 时间复杂度: \(O(n\log^2{n})\) 空间复杂度: \(O(n\log{n})\) 代码 #include <bits/s

题解 luogu P1501【[国家集训队]Tree II】(Link-Cut-Tree)

Link-Cut-Tree 的懒标记下传正确食用方法. 我们来逐步分析每一个操作. 1:+ u v c:将u到v的路径上的点的权值都加上自然数c; 解决方法: 很显然,我们可以 split(u,v) 来提取u,v这一段区间,提取完了将 Splay(v),然后直接在v上打加法标记add即可. 代码: inline void pushadd(ll x,ll val){//打标记 s[x]+=sz[x]*val,v[x]+=val,add[x]+=val; s[x]%=MOD,v[x]%=MOD,ad

[题解] luogu P1985 [USACO07OPEN]翻转棋

题面 今天学搜索,正好水一发以前做的这道毒瘤题 话说这道题做了我一天,别人都是各种优化,不超100行 天真的我硬核刚了220行,全程0优化水过 但其实不用这么长,我有的函数写的有点重复了( 思路: 显然是dfs,一行一行的来 搜到[i, j]时(i > 1),看[i - 1, j]是否为黑,是的话就翻转[i, j], 也就是说搜完当前行就要保证上一行的棋全都翻成了白色 当搜到最后一行时, 既要保证上一行翻成白色,还要保证自己也都翻成白色, 最后还要特判一下最后两个的翻转. 当时年少轻狂,我想着层

[题解] luogu P1514 引水入城 DFS + 贪心覆盖区间

原题链 打了一上午,我真是弱爆了 看完题目,可以很显然的想到一种搜法: DFS/BFS第1个到第m个临湖城市,求出干旱区城市能否全有水,很显然,这样时间会炸 此时,我们可以在选择搜第i个临海城市加一个剪枝: if (c[1][i-1] < c[1][i] && c[1][i+1] < c[1][i]) dfs(); 意思说,当且仅当 i 的前一个临湖城市和后一个临海城市的海拔都小于当前临湖城市海拔,我才去搜这个城市 为什么? 你会发现,一个临湖城市要是比自己相邻的临湖城市海拔小