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)\to 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\n",ans[i]);
    }


}

int main()
{
    zzc::work();
    return 0;
}
posted @ 2021-01-05 14:58  youth518  阅读(59)  评论(0编辑  收藏  举报