• P3348 [ZJOI2016]大森林 LCT


    P3348 [ZJOI2016]大森林 LCT

    题意:

    戳这里

    分析:

    巨佬1 说这是水题,应该一眼切,我还是太菜了/kk

    不讲思考过程了,因为我压根就不会,思考了寂寞

    具体做法就是,我们发现询问可以离线,那么对操作进行排序,利用LCT的动态维护的功能就可以在一棵树上通过删改得到所有情况的答案

    我们开始分操作考虑,首先我们发现一个不是很显然的性质:所有询问的点对一定通过实际存在的点相连(按巨佬的话说,就是在一个“连通块里”),也就是说查询路径上的点一定是实际存在的

    由此我们可以发现 (0) 操作,可以看作全局生长,因为你在其他生长节点上长上去的点不是实际存在的节点,不会影响别的询问

    接着考虑 (1) 操作,我们将该生长节点影响的区间((l,r))拿出来,然后我们发现对于一个 (1 l r x) 的操作,当扫到第 (l) 棵树的时候,需要把 (l-1) 这棵树上生长节点所连的同时属于 (l) 这棵树的节点 连到 (x) 上,也就是说我们需要让 (LCT) 支持修改子树父亲这一操作,其实可以直接 (ETT), 但是我们可以通过一个小 (trick) 实现,就是设立虚点,将需要修改的节点连在一个没有任何信息的虚点上,然后直接对虚点进行修改相当于同时修改了它的子树

    然后考虑 (2) 操作,相当于用 (lct) 查询任意两点的 (lca) ,具体实现就是先 (access(x)) 然后 (access(y)) 的返回值就是(lca) ,然后我们还要查询树上两点间的距离,其实我们可以直接用 (siz) 就能表示出 (dep) ,我们 (access(x) o splay(x))(x)(siz) 就是它内部节点数

    tip:

    1. 对于 (1) 操作的边界,一定要用 (x) 存在的左右边界和询问的左右边界进行取min,max ,这个边界一定要求对,因为我们把 (0) 操作全局化了,所以必须保证询问合法,不然会 (WA)

    2. 可以把 (0,1) 两个操作看成同一种操作,(0) 操作相当于连一个虚点然后虚点连接了一个点权为 (1) 的实点, (1) 操作相当于连一个虚点然后虚点连一个点权为 (0) 的虚点当做生长节点,由于点权为 (0) 所以在距离上来说,这个新的生长节点和原来的生长节点是同一个,不会对答案造成影响

    3. 因为最开始所有的树的生长节点都是 (1) 所以我们可以看成最开始有一个操作 (1 1 n 1) ,所以我们最开始要新建一个节点 (1) 作为生长节点,建一个节点 (2) 作为 (1) 所连的虚点

    4. 由于实点的父亲不会变,一定是在他之前离他最近的那个生长节点,所以我们可以一开始就把所有的 (0) 操作处理完,建出所有的实点

    5. 由于这个题默认以 (1) 为树根当做起始生长节点,所以树边是有向的,不能进行 (makeroot,pushdown,split) 等会改变树形态的操作

    代码:

    #include<bits/stdc++.h>
    
    using namespace std;
    
    namespace zzc
    {
        int read()
        {
            int x=0,f=1;char ch=getchar();
            while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
            while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
            return x*f;
        }
    
        const int maxn = 1e6+5;
        int n,m,idx,qt,tot,lst;
        int lef[maxn],rig[maxn],id[maxn],ans[maxn];
        namespace lct
        {
            int cnt,top;
            int qu[maxn],val[maxn],sum[maxn],ch[maxn][2],fa[maxn],rev[maxn];
            void new_node(int x){cnt++;val[cnt]=sum[cnt]=x;}
            bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
            void pushup(int x){sum[x]=sum[ch[x][0]]+sum[ch[x][1]]+val[x];}
            void pushdown(int x){int l=ch[x][0],r=ch[x][1];if(rev[x]){swap(ch[x][0],ch[x][1]),rev[l]^=1,rev[r]^=1,rev[x]=0;}}
            void rotate(int x)
            {
                int y=fa[x],z=fa[y],l,r;
                if(ch[y][0]==x)l=0;else l=1;r=l^1;
                if(!isroot(y)){if(ch[z][0]==y)ch[z][0]=x;else ch[z][1]=x;}
                fa[x]=z;fa[y]=x;fa[ch[x][r]]=y;
                ch[y][l]=ch[x][r];ch[x][r]=y;
                pushup(x);pushup(y);
            }
            void splay(int x)
            {
                qu[top=1]=x;
                for(int i=x;!isroot(i);i=fa[i])qu[++top]=fa[i];
                for(int i=top;i;i--) pushdown(qu[i]);
                while(!isroot(x))
                {
                    int y=fa[x],z=fa[y];
                    if(!isroot(y))
                    {
                        if((ch[z][0]==y)^(ch[y][0]==x))rotate(x);
                        else rotate(y);
                    }
                    else rotate(x);
                }
                pushup(x);
            }
            int access(int x){int t=0;for(;x;t=x,x=fa[x]){splay(x);ch[x][1]=t;pushup(x);}return t;}
            void makeroot(int x){access(x);splay(x);rev[x]^=1;}
            void link(int x,int y){splay(x);fa[x]=y;}
            void cut(int x){access(x);splay(x);fa[ch[x][0]]=0,ch[x][0]=0,pushup(x);}
            int query(int x,int y)
            {
                int res=0,lca;
                access(x);splay(x);res+=sum[x];
                lca=access(y);splay(y);res+=sum[y];
                access(lca);splay(lca);res-=2*sum[lca];
                return res;
            }
        }
        using namespace lct;
        
        struct que
        {
            int pos,opt,x,y;
            que(){}
            que(int pos,int opt,int x,int y):pos(pos),opt(opt),x(x),y(y){}
            bool operator <(const que &b)const
            {
                return pos==b.pos?opt<b.opt:pos<b.pos;
            }
        }q[maxn];
    
        void work()
        {
            int opt,x,y,z;
            n=read();m=read();
            new_node(1);lef[1]=1;rig[1]=n;id[idx=1]=1;
            new_node(0);link(2,1);lst=2;
            for(int i=1;i<=m;i++)
            {
                opt=read();
                if(opt==0)
                {
                    x=read();y=read();
                    new_node(1);lef[++idx]=x;rig[idx]=y;id[idx]=cnt;
                    q[++tot]=que(1,i-m,cnt,lst);
                }
                else if(opt==1)
                {
                    x=read();y=read();z=read();
                    x=max(x,lef[z]);y=min(y,rig[z]);
                    if(x<=y)
                    {
                        new_node(0);link(cnt,lst);
                        q[++tot]=que(x,i-m,cnt,id[z]);
                        q[++tot]=que(y+1,i-m,cnt,lst);
                        lst=cnt;
                    }
                }
                else if(opt==2)
                {
                    x=read();y=read();z=read();
                    q[++tot]=que(x,++qt,id[y],id[z]);
                }
            }
            sort(q+1,q+tot+1);
            for(int i=1,j=1;i<=n;i++)
            {
                for(;i==q[j].pos;j++)
                {
                    if(q[j].opt<0) cut(q[j].x),link(q[j].x,q[j].y);
                    else ans[q[j].opt]=query(q[j].x,q[j].y);
                }
            }
            for(int i=1;i<=qt;i++) printf("%d
    ",ans[i]);
        }
    
    
    }
    
    int main()
    {
        zzc::work();
        return 0;
    }
    
  • 相关阅读:
    今日🎸吉他练到这儿
    自然对数
    交叉编译ffmpeg
    ffmpeg rtsp推流
    上新啦!DevExpress WinForm最新免费中文视频汇总(基础+应用)
    Vue框架的原生UI组件Kendo UI for Vue——主题 & 样式概述
    界面控件Telerik R3 2022新版亮点 全新的ThemeBuilder工具
    来看界面控件DevExtreme如何实现数据表单的高效动态更新
    「盘点」2022年TOP 5的UI & UX设计趋势
    如何使用界面控件DevExpress WinForms自带的UI模板?其实很简单
  • 原文地址:https://www.cnblogs.com/youth518/p/14235842.html
Copyright © 2020-2023  润新知