• 洛谷P3348 [ZJOI2016]大森林(LCT,虚点,树上差分)


    传送门

    还是要膜拜一下hjt大佬啊->这里

    又学(抄)了一个新姿势:虚点

    先考虑暴力,直接从左到右link,然后T飞

    可以发现如果没有1操作,所有的树的结构都是一样的(即对于询问来说并没有影响)

    不难看出,离线先处理所有操作,再回答询问是没有问题的(因为操作只会在下面加节点,不会影响树上两点之间的距离)

    考虑一下1操作,假设有一个形如$1 x l r$的操作,从微观上来看会有什么影响呢?先考虑第l-1棵树和第l棵树,相当于是把l-1棵树上原来生长节点的所有子节点取下来,接到l棵树上新的生长节点上去,同样,对第r棵树和第r+1棵树,相当于是把这些节点又接了回来。

    现在的问题就是怎么快速转移一棵树的子树了(听说大佬们会有ETT然而蒟蒻表示听都没听过)

    就是考虑建一个虚点。对于每一个1操作,我们在原来的生长节点下面建一个虚点,然后此后所有的0操作都将节点建在虚点下面,这样一来要转移子树时只需把虚点和父亲的连接断开在向右接过去就行了。于是1操作可以拆成l-1->l和r->r+1的两个操作了

    一开始默认所有虚点都在一起,把所有操作离线,按端点为第一关键字,时间为第二关键字排序,从左到右处理一遍就行了

    然后问题是怎么查询距离?我们可以设实点(0操作加的点)值为1,虚点值为0。然而split什么的是不可以的,因为会打乱原来的父子关系,而且他们的LCA可能是虚点。那就树上差分吧。距离就是$sum[x]+sum[y]-2*sum[LCT中的LCA]$

    然而怎么在LCT求LCA?就是access(x)再access(y),access(y)过程中跳到的最后一条虚边的右儿子就是他们的LCA了(就是跳access过程中x=0时的y)(因为access(x)的时候相当于把x到根节点的路径全都标记为实边,在access(y)的时候每一次都跳一条虚边,跳完最后一条虚边就到LCA了)(大概?我也不是很清楚)

      1 //minamoto
      2 #include<cstdio>
      3 #include<iostream>
      4 #include<algorithm>
      5 using std::sort;
      6 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
      7 char buf[1<<21],*p1=buf,*p2=buf;
      8 template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
      9 template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
     10 inline int read(){
     11     #define num ch-'0'
     12     char ch;bool flag=0;int res;
     13     while(!isdigit(ch=getc()))
     14     (ch=='-')&&(flag=true);
     15     for(res=num;isdigit(ch=getc());res=res*10+num);
     16     (flag)&&(res=-res);
     17     #undef num
     18     return res;
     19 }
     20 char obuf[1<<24],*o=obuf;
     21 void print(int x){
     22     if(x>9) print(x/10);
     23     *o++=x%10+48;
     24 }
     25 const int N=300005;
     26 #define qr(a,b,c,d) qry[++cnt]=(q){a,b,c,d}
     27 struct q{
     28     int pos,opt,x,y;
     29     inline bool operator <(const q &b)const
     30     {return pos<b.pos||(pos==b.pos&&opt<b.opt);}
     31 }qry[N];
     32 int fa[N],ch[N][2],gl[N],gr[N],sum[N],at[N],ans[N];
     33 bool v[N];
     34 inline bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
     35 #define ls ch[x][0]
     36 #define rs ch[x][1]
     37 inline void pushup(int x){sum[x]=sum[ls]+sum[rs]+v[x];}
     38 void rotate(int x){
     39     int y=fa[x],z=fa[y],d=ch[y][1]==x;
     40     if(!isroot(y)) ch[z][ch[z][1]==y]=x;
     41     fa[x]=z,fa[y]=x,fa[ch[x][d^1]]=y,ch[y][d]=ch[x][d^1],ch[x][d^1]=y,pushup(y);
     42 }
     43 void splay(int x){
     44     for(int y=fa[x],z=fa[y];!isroot(x);y=fa[x],z=fa[y]){
     45         if(!isroot(y))
     46         ((ch[y][1]==x)^(ch[z][1]==y))?rotate(x):rotate(y);
     47         rotate(x);
     48     }
     49     pushup(x);
     50 }
     51 int access(int x){
     52     int y=0;
     53     for(;x;x=fa[y=x]){
     54         splay(x),rs=y,pushup(x);
     55     }
     56     return y;
     57 }
     58 void cut(int x){
     59     access(x),splay(x),ls=fa[ls]=0;pushup(x);
     60 }
     61 void link(int x,int y){
     62     splay(x),fa[x]=y;
     63 }
     64 int main(){
     65     //freopen("testdata.in","r",stdin);
     66     int n,m,cnt=0,tot=0,real,aux,p;
     67     n=read(),m=read();
     68     real=v[1]=sum[1]=gl[1]=at[1]=1,gr[1]=n;
     69     link(p=aux=2,1);
     70     for(int i=1;i<=m;++i){
     71         int op=read(),l=read(),r=read();
     72         switch(op){
     73             case 0:{
     74                 link(at[++real]=++p,aux),v[p]=sum[p]=1;
     75                 gl[real]=l,gr[real]=r;
     76                 break;
     77             }
     78             case 1:{
     79                 int x=read();
     80                 cmax(l,gl[x]),cmin(r,gr[x]);
     81                 if(l>r) continue;
     82                 link(++p,aux);
     83                 qr(l,i-m,p,at[x]),qr(r+1,i-m,p,aux);
     84                 aux=p;
     85                 break;
     86             }
     87             case 2:{
     88                 int x=read();
     89                 qr(l,++tot,at[r],at[x]);
     90                 break;
     91             }
     92         }
     93     }
     94     sort(qry+1,qry+1+cnt);
     95     for(int i=1;i<=cnt;++i){
     96         if(qry[i].opt>0){
     97             int x,y,l,r;
     98             access(x=qry[i].x),splay(x),r=sum[x];
     99             l=access(y=qry[i].y),splay(y),r+=sum[y];
    100             access(l),ans[qry[i].opt]=r-(sum[l]<<1);
    101         }
    102         else cut(qry[i].x),link(qry[i].x,qry[i].y);
    103     }
    104     for(int i=1;i<=tot;++i)
    105     print(ans[i]),*o++='
    ';
    106     fwrite(obuf,o-obuf,1,stdout);
    107     return 0;
    108 }
  • 相关阅读:
    面试题系列--【vue的生命周期】
    面试题系列--【hash和history的区别】
    ES6系列--【事件循环 EventLoop(Promise,setTimeOut,async/await执行顺序)】
    ES6系列--【ES6数组新增方法】
    ES6系列--【ES6 新增字符串方法】
    微信小程序系列--【VXML语法、VMSS样式、条件渲染、列表渲染、模板、引用、组件、事件系统】
    微信小程序系列--【小程序注册、工程创建、全局配置、页面配置】
    微信小程序系列---【五星好评案例】
    react系列---【Hooks】
    2019.9.18-单向循环链表删除元素(+之前完整代码)
  • 原文地址:https://www.cnblogs.com/bztMinamoto/p/9434542.html
Copyright © 2020-2023  润新知