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;
}
原文地址:https://www.cnblogs.com/youth518/p/14235842.html