Top Tree

总归要学的。
先写一下理解比较困难的点。

  1. 考虑 SATT 的建立过程:首先在树里面找到一个Compress Tree,这个树满足中序遍历写下来是根簇路径深度从小到大排列,然后根簇路径上挂着的小簇Rake起来,这个 Rake 的过程是容易的,考虑对于每一个直接连接的小簇,把他变成子问题,然后给一个代表点(Rake Node),和子问题的 Compress Tree 的根节点连接,然后这些代表点一个一个连在一起,依照合并顺序变成 Rake Tree,我们可以认为 Rake Node 代表的是这里有一个小簇等待连边,而很明显Compress结点和Rake结点万万不能混在一起,所以我们就他们内部用二叉树各自维护平衡(使用 Splay),而中间使用三叉树,用中间结点将他们连接。
  2. 考虑信息的维护过程,我们已经用Rake Tree 将原树切割成了若干个 Compress Tree,也就是若干链,对于一个Compress Node,他的子树中的左右儿子代表的是他的当前子树簇路径的两个端点,而中儿子代表他挂的小簇的端点,这个是Rake下来的,考虑这个就是簇路径之外的点,于是我们可以用这个东西维护簇路径信息和簇信息,考虑我们能够通过expose强制把两个点塞根簇的端点里,也就是说我们可以这样维护路径信息,而子树信息也很好维护,因为我们可以维护子树簇内非子树簇路径的点的权值和,那我们只需要把点 x 做一次 access 之后对 x 的子树的非路径信息做操作,再合并 x 的信息即可,因为 x 是端点,端点的话子树内非簇信息就是子树啊。
  3. Access 操作实际上是这样的,对于一个点他从属于一条 Compress Tree,而 Compress Tree 除非根簇上面都有一个 Rake Node,那么我们需要做得操作就是首先把当前点提到 Compress Tree 的根,然后操縦 Rake Node 到 Rake Tree 的根结点,这个时候我们再把 x 的父亲的父亲提到他所在的 Compress Tree 的根结点,此时如果这个点还有右结点,我们可以直接互换,否则的话我们就把 x 直接点过去并删除当前的 Rake Node。
    而原理也很易懂,考虑Compress Tree要求中序遍历下正好深度由当前钦点的同上层 Compress Tree 相连的结点往下递增,而我们做这样的操作的意思是,如果还有右端点(也就是在上一层的下端结点),就直接交换,将上一层的 Compress Node 作一个连接,相当于是将他的末尾端点引导过来,而上一层如果没有那就是延长末尾到当前簇,那当前簇就没必要再通过 Rake 操作进入树上了,可以直接进入上一层的 Compress Tree,所以我们直接删除当前的Rake结点即可。
    我们都知道根簇应该有两个端点,其中一个是根结点,而 Access(x) 的作用就是使 x 成为根簇中的非根结点端点,这个很有用的,而Makeroot(x) 的作用则是把 x 变成根簇的根结点端点注意到我们是可以控制根簇端点的,着可以让我们维护很多东西!
  4. 连边和删边,其实也很简单,我们先做一下 makeroot(x)将 x 提到根结点,再access(y)将y提到根结点,具体区别见上,然后我们现在就是要在让两个根簇连接在一起,怎么连接呢,考虑相当于是做 compress 操作,x 现在是根结点而 y 现在是末尾,而具体路径结构是怎么接都不会变的,我们的需求是让路径的开头也就是 x和路径的结尾也就是 y 直接接到一起(具体操作是 y 的右儿子接 x)这样就认为连上了边!
    而基簇怎么办?基簇考虑对于Compress Tree,一个原树结点的作用是 Compress 或者是不做(端点),而 我们又知道对于一条边进入 Compress Tree 的方式只能是通过点的代表点,那这里结果就出来了,把基簇放到 x 的左儿子上,这样的话中序遍历合并整体就是对的,具体的合并是很优美的。
    可惜 LCT 板子只需要我们维护点权,基簇维护与否我们根本不在乎。
  5. 标记下传,有点弯弯绕绕,实际上我们的标记要分类,路径标记只能传两边,子树标记能传两边和中间,而维护的信息也是分簇路径信息和非簇路径信息两种。
#include<bits/stdc++.h>
#define ls(x) ch[x][0]
#define rs(x) ch[x][1]
#define ms(x) ch[x][2]
using namespace std;
class TopTree{
   public:
   static const int MAXN=600005;
   int ch[MAXN*3][3];
   int father[MAXN*3];
   int edgeval[MAXN*3];
   int pathval[MAXN*3];
   int val[MAXN*3];
   bool revtag[MAXN*3];
   int top,tot;
   int trash[MAXN*3];
   int newnode(int x){
      int tmp;
      if(top)tmp=trash[top--];
      else tmp=++tot;
      val[tmp]=x;
      pushup(tmp);
      return tmp;
   }
   bool nroot(int x){
      return ls(father[x])==x||rs(father[x])==x;
   }
   int get(int x){
      return ms(father[x])==x?2:rs(father[x])==x;
   }
   void pushup(int x){
      edgeval[x]=edgeval[ls(x)]^edgeval[rs(x)]^edgeval[ms(x)]^val[x];
      pathval[x]=pathval[ls(x)]^pathval[rs(x)]^val[x];//
      return;
   }
   void rotate(int x){
      int y=father[x],z=father[y];
      int k=get(x);
      if(z)ch[z][get(y)]=x;
      ch[y][k]=ch[x][k^1];
      if(ch[x][k^1])father[ch[x][k^1]]=y;
      ch[x][k^1]=y;
      father[y]=x;
      father[x]=z;
      pushup(y);
      pushup(x);
      return;
   }
   void reverse(int x){
      revtag[x]^=1;
      swap(ch[x][0],ch[x][1]);
      return;
   }
   void add(int x,int w){
      val[x]^=w;
      pushup(x);
      return;
   }
   void pushdown(int x){
      if(!revtag[x])return;
      reverse(ch[x][0]),reverse(ch[x][1]);
      revtag[x]=false;
      return;
   }
   void update(int cur){
      if(nroot(cur))update(father[cur]);
      pushdown(cur);
      return;
   }
   void splay(int x,int goal=0){
      update(x);
      for(int now=father[x];now=father[x],nroot(x)&&(father[x]!=goal);rotate(x)){
         if(nroot(now)&&father[now]!=goal){
            if(get(now)==get(x)){
               rotate(now);
            }
            else{
               rotate(x);
            }
         }
      }
      pushup(x);
      return;
   }
   void clear(int &cur){
      ch[cur][0]=ch[cur][1]=ch[cur][2]=0;
      val[cur]=0;
      revtag[cur]=0;
      father[cur]=0;
      edgeval[cur]=0;
      pathval[cur]=0;
      trash[++tot]=cur;
      cur=0;
      return;
   }
   void setthefather(int x,int fa,int type){
      if(x)father[x]=fa;
      ch[fa][type]=x;
      return;
   }
   void del(int x){
      if(!ch[x][0]){
         setthefather(rs(x),father[x],2);
         clear(x);
         return;
      }
      int now=ls(x);
      pushdown(now);
      while(rs(now)){
         now=rs(now);
         pushdown(now);
      }
      splay(now,x);
      setthefather(rs(x),now,1);
      setthefather(now,father[x],2);
      pushup(now);
      pushup(father[x]);
      clear(x);
      return;
   }
   void Splice(int x){
      splay(x);
      int y=father[x];
      splay(y);
      pushdown(x);
      if(rs(y)){
         swap(father[rs(y)],father[ms(x)]);
         swap(rs(y),ms(x));
         pushup(x);
      }
      else{
         setthefather(ms(x),father[x],1);
         del(x);
      }
      pushup(rs(y));
      pushup(y);
      return;
   }
   void access(int x){
      splay(x);
      int tmp=x;
      if(rs(x)){
         int y=newnode(0);
         setthefather(rs(x),y,2);
         setthefather(ms(x),y,0);
         setthefather(y,x,2);
         rs(x)=0;
         pushup(y);
         pushup(x);
      }
      while(father[x]){
         Splice(father[x]);
         x=father[x];
      }
      splay(tmp);
      return;
   }
   void makeroot(int x){
      access(x);
      reverse(x);
      return;
   }
   void expose(int x,int y){
      makeroot(x);
      access(y);
      return;
   }
   int findroot(int x){
      access(x);
      pushdown(x);
      while(ls(x))x=ls(x),pushdown(x);
      splay(x);
      return x;
   }
   void link(int x,int y){
      if(findroot(x)==findroot(y))return;
      expose(x,y);
      int z=newnode(0);
      setthefather(x,y,1);
      setthefather(z,x,0);
      pushup(x);
      pushup(y);
      return;
   }
   void cut(int x,int y){
      expose(x,y);
      if(ls(y)!=x||father[x]!=y||ls(rs(x))||ms(rs(x))||rs(rs(x))){
         return;
      }
      clear(rs(x));
      father[x]=0;
      ls(y)=0;
      pushup(y);
      pushup(x);
      return;
   }
}P;
int n,m;
int main(){
   // freopen("test.in","r",stdin);
   // freopen("test.out","w",stdout);
   scanf("%d%d",&n,&m);
   P.tot=n;
   for(int i=1;i<=n;i++){
      int x;
      scanf("%d",&x);
      P.val[i]=x;
   }
   while(m--){
      int opt,x,y;
      scanf("%d%d%d",&opt,&x,&y);
      switch(opt){
         case 0:{
            P.expose(x,y);
            printf("%d\n",P.pathval[y]);
            break;
         }
         case 1:{
            P.link(x,y);
            break;
         }
         case 2:{
            P.cut(x,y);
            break;
         }
         case 3:{
            P.access(x);
            P.val[x]=y;
            P.pushup(x);
            break;
         }
      }
   }
   return 0;
}

一点分讨都没有的动态树模板代码,维护了基簇与子树簇路径的答案和非子树簇路径的答案。

posted @ 2023-11-27 21:12  Yanami_Anna  阅读(274)  评论(0)    收藏  举报