[UOJ195]大森林(LCT建虚点)

小Y家里有一个大森林,里面有 n n 棵树,编号从 1 n n 。一开始这些树都只是树苗,只有一个节点,标号为 1 。这些树都有一个特殊的节点,我们称之为生长节点,这些节点有生长出子节点的能力。

小Y掌握了一种魔法,能让第 l l 棵树到第 r 棵树的生长节点长出一个子节点。同时她还能修改第 l l 棵树到第 r 棵树的生长节点。

她告诉了你她使用魔法的记录,你能不能管理她家的森林,并且回答她的询问呢?

输入格式

第一行包含 2 个正整数 n,m n , m ,共有 n n 棵树和 m 个操作。

接下来 m m 行,每行包含若干非负整数表示一个操作,操作格式为:

  1. 0 l l r 表示将第 l l 棵树到第 r 棵树的生长节点下面长出一个子节点,子节点的标号为上一个 0 0 号操作叶子标号加 1(例如,第一个 0 0 号操作产生的子节点标号为 2), l l r 之间的树长出的节点标号都相同。保证 1lrn 1 ≤ l ≤ r ≤ n

    • 1 1 l r r x 表示将第 l l 棵树到第 r 棵树的生长节点改到标号为 x x 的节点。对于区间内的每棵树,如果标号 x 的点不在其中,那么这个操作对该树不产生影响。保证 1lrn 1 ≤ l ≤ r ≤ n x x 不超过当前所有树中节点最大的标号。
    • 2 x x u v v 询问第 x 棵树中节点 u u 到节点 v 的距离,也就是在第 x x 棵树中从节点 u 和节点 v v 的最短路上边的数量。保证 1xn,这棵树中节点 u u 和节点 v 存在。
    • 输出格式

      输出包括若干行,按顺序对于每个小Y的询问输出答案。

      样例一

      input
      5 5
      0 1 5
      1 2 4 2
      0 1 4
      2 1 1 3
      2 2 1 3
      
      
      output
      1
      2
      
      

      样例二

      见样例数据下载。

      限制与约定

      测试点编号 n n m约定
      1 103 ≤ 10 3 103 ≤ 10 3
      2 105 ≤ 10 5 2×105 ≤ 2 × 10 5 保证每次 0 0 1 操作修改的是 1 1 n 所有的树
      3
      4保证每次 0 0 操作生长节点都是这些树中编号最大的节点
      5
      6
      7
      8
      9
      10

      时间限制:2s

      空间限制: 256MB 256 MB


      思路&&分析:

          对于每一个1操作,我们可以建一个虚点,然后对于每个0操作都将这些点挂在[在这个操作前面最迟的1操作上建的虚点]的下面。于是我们可以将操作离线,然后从左到右把所有树都扫一遍就行了,这样一来我们打一颗LCT就完事了。

      Code

      #pragma GCC optimize(3)
      #include<bits/stdc++.h>
      using namespace std;
      typedef long long ll;
      bool Finish_read;
      template<class T>inline void read(T &x){Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
      template<class T>inline void print(T x){if(x/10!=0)print(x/10);putchar(x%10+'0');}
      template<class T>inline void writeln(T x){if(x<0)putchar('-');x=abs(x);print(x);putchar('\n');}
      template<class T>inline void write(T x){if(x<0)putchar('-');x=abs(x);print(x);}
      /*================Header Template==============*/
      const int maxn=200005;
      int c[maxn][2],sz[maxn],val[maxn],fa[maxn],p,cnt,m,n,tot,le[maxn],ri[maxn],id[maxn],now,ans[maxn];
      struct Event {
          int pos,op,x,y;
          Event(){}
          Event(const int &pos,const int &op,const int &x,const int &y):pos(pos),op(op),x(x),y(y){}
          inline bool operator < (const Event &rhs) const {
              return pos==rhs.pos?op<rhs.op:pos<rhs.pos;
          }
      }a[maxn<<2];
      inline void pushup(int x) {
          int l=c[x][0],r=c[x][1];
          sz[x]=sz[l]+sz[r]+val[x];
      }
      inline bool isrt(int x) {
          return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;
      }
      inline void rotate(int x) {
          int y=fa[x],z=fa[y],l=(c[y][1]==x),r=l^1;
          if(!isrt(y))
              c[z][c[z][1]==y]=x;
          fa[y]=x,fa[x]=z;
          fa[c[x][r]]=y;
          c[y][l]=c[x][r];
          c[x][r]=y;
          pushup(y),pushup(x);
      }
      inline void splay(int x) {
          while(!isrt(x)) {
              int y=fa[x],z=fa[y];
              if(!isrt(y)) {
                  if(c[y][0]==x^c[z][0]==y)
                      rotate(x);
                  else
                      rotate(y);
              }
              rotate(x);
          }
      }
      inline int access(int x) {
          int t=0;
          for(;x;t=x,x=fa[x])
              splay(x),c[x][1]=t,pushup(x);
          return t;
      }
      inline void cut(int x) {
          access(x);
          splay(x);
          c[x][0]=fa[c[x][0]]=0;
          pushup(x);
      }
      inline void link(int x,int y) {
          splay(x);
          fa[x]=y;
      }
      inline void add(int o) {
          sz[n+1]=val[++n]=o;
      }
      int main() {
          read(p),read(m);
          add(1),cnt=1,le[cnt]=id[cnt]=1,ri[cnt]=p,add(0),now=2,link(2,1);
          memset(ans,-1,sizeof ans);
          for(int i=1,op,k,x,y;i<=m;i++) {
              read(op);
              if(op==0) {
                  read(x);read(y);++cnt;
                  le[cnt]=x,ri[cnt]=y,add(1),id[cnt]=n;
                  a[++tot]=Event(1,i-m,n,now);
              }
              else if(op==1) {
                  read(x),read(y),read(k);
                  x=max(x,le[k]),y=min(y,ri[k]);
                  if(x<=y) {
                      add(0);
                      if(x>1)
                          link(n,now);
                      a[++tot]=Event(x,i-m,n,id[k]);
                      a[++tot]=Event(y+1,i-m,n,now); 
                      now=n;
                  }
              }
              else {
                  read(k),read(x),read(y);
                  a[++tot]=Event(k,i,id[x],id[y]);
              }
          }
          sort(a+1,a+tot+1);
          for(int i=1,k=1;i<=p;i++)
              for(;k<=tot&&a[k].pos==i;k++)
                  if(a[k].op>0) {
                      access(a[k].x),splay(a[k].x),ans[a[k].op]=sz[a[k].x];
                      int t=access(a[k].y);splay(a[k].y),ans[a[k].op]+=sz[a[k].y];
                      access(t),splay(t),ans[a[k].op]-=sz[t]<<1;
                  }
                  else {
                      cut(a[k].x);
                      link(a[k].x,a[k].y);
                  }
          for(int i=1;i<=m;i++)
              if(ans[i]!=-1)
                  printf("%d\n",ans[i]);
          return 0;
      }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值