Lct浅谈

1.对lct的认识

​  首先要知道$lct$是什么。$lct$的全称为$link-cut-tree$。通过全称可以看出,这个数据结构是维护树上的问题,并且是可以支持连边断边操作。$lct$是一种动态树,可以处理动态森林问题,并支持查询与修改。

2.前置知识

  在学习$lct$之前,建议先学习一下树链剖分,以及树链剖分套线段树,因为$lct$的思想与其相似,且树链剖分套线段树维护的是静态树,并且好实现。并且在$lct$的代码实现中我们要用到$splay$这个小小的知识点,所以建议学一下。

3.$lct$的讲解

​  我们先看树链剖分套线段树,这是先将整棵树构建好,我们直接在构建的树上进行树剖,并在树剖序上建出线段树,直接在线段树上乱搞就好了。我们想怎么能将静态树变成动态树呢?由于线段树是静态的,不能支持插入,所以我们需要改变我们所运用的数据结构,我们将线段树改变成为$splay$。

​  在$lct$中我们需要讲解八个操作。在讲解这些操作之前,我们需要知道一些名词以及其含义。

​  1) 辅助树与虚子树:我们在$lct​$这个算法中要运用到$splay​$,这个$splay​$的运用之处与树剖中线段树的运用之处基本相似。$splay​$是用来维护这棵树上的链,一个链就是一个$splay​$。这些$splay​$所构成的东西就叫辅助树。维护辅助树的关键字是当前节点的深度,也就是一个$splay​$上的一个节点的右儿子的子树中的所有点的深度都要比当前点的深度小,反之都大。但是我们用$splay​$维护了链,这些$splay​$怎么连到一起?我们在维护$splay​$的时候要记录当前节点的父亲,到整棵$splay​$的根节点是没有的父亲的,这时我们将这个根的父亲指到整个链顶在原树中的父亲就好了,这是我们称原树中的链顶的父亲有一个虚子树,这棵虚子树就是整个链的$splay​$。

​  知道上面两个名词之后,我们来讲解一下操作:

​  1) $Splay$:这个操作就是将当前节点旋转成为当前节点所在$splay$的根。这个操作要配合$rotate$来实现,这两个函数和正常的$splay$没有太大的区别,只是判根不一样,这里的判根是看当前节点的父亲的两个儿子是否都不等于自己,如果是则为根,反之不是。

  1. bool check(int p) {return son[fa[p]][1]==p;}
  2. bool isroot(int p) {return son[fa[p]][0]!=p&&son[fa[p]][1]!=p;}
  3. void rotate(int p)
  4. {
  5. int tmp1=fa[p],tmp2=fa[tmp1],tmp3=check(p);
  6. fa[son[tmp1][tmp3]=son[p][tmp3^1]]=tmp1,fa[p]=tmp2;
  7. if(!isroot(tmp1)) son[tmp2][check(tmp1)]=p;
  8. fa[son[p][tmp3^1]=tmp1]=p,pushup(tmp1),pushup(p);
  9. }
  10. void splay(int p)
  11. {
  12. update(p);
  13. for(int i;i=fa[p],!isroot(p);rotate(p))
  14. if(!isroot(i)) rotate(check(p)==check(i)?i:p);
  15. }

  2) $Access$:这个操作是将当前节点到当前节点所在辅助树的根的链变为一颗$splay$,代码不难理解。

  1. void access(int p)
  2. {
  3. for(int t=0;p;t=p,p=fa[p]) splay(p),pushup(p);
  4. }

  3) $Makeroot$:这个操作是将当前节点变为当前节点所在辅助树的根。显然这个操作就是将当前节点先$Access$一下,再将当前节点$Splay$一下。由于希望保证时间复杂度,所以我们将当前

  1. void makeroot(int p)
  2. {
  3. access(p),splay(p),swap(son[p][0],son[p][1]),rev[p]^=1;
  4. }

  4) $Update$:这个函数的功能在上面已经提到。

  1. void pushdown(int p)
  2. {
  3. if(!rev[p]) return;
  4. swap(son[son[p][0]][0],son[son[p][0]][1]);
  5. swap(son[son[p][1]][0],son[son[p][1]][1]);
  6. rev[son[p][0]]^=1,rev[son[p][1]]^=1,rev[p]=0;
  7. }
  8. void update(int p)
  9. {
  10. if(!isroot(p)) update(fa[p]);pushdown(p);
  11. }

  5) $Pushup$:这个操作就是用来统计答案的,类似于线段树上的$pushup$。

  1. void pushup(int p)
  2. {
  3. if(p) size[p]=1+size[son[p][1]]+size[son[p][0]];
  4. }

  6) $Link$:这个操作就是连边,将$x,y$连接在一起。我们需要先$Makeroot(x)$,并$Makeroot(y)$。这样我们就可以直接将$fa[x]=y$,这样我们将以$x$为根的辅助树变为以$y$为根的辅助树的虚子树,这样就解决了。

  1. void link(int x,int y)
  2. {
  3. makeroot(x),makeroot(y),fa[x]=y,ord[y]+=size[x];
  4. }

  7) $Cut$:这个操作就是断边,将$x,y$所连接的边断开。我们先$Makeroot(x)$,并$Access(y),Splay(y)$,这时$y$的左儿子就是$x$,所以只需要$son[y][0]=fa[x]=0$就好了。

  1. void cut(int x,int y)
  2. {
  3. makeroot(x),access(y),splay(y),son[y][0]=fa[x]=0;
  4. }

  8) $Find$:这个操作是用来寻找当前节点所在辅助树中的根是谁。

  1. int find(int x)
  2. {
  3. access(x),splay(x);
  4. while((son[x][0]&&rev[x]==false)||(son[x][1]&&rev[x])) pushdown(x),x=son[x][0];
  5. return x;
  6. }

  整合一下,就是下面的模板:

  1. bool check(int p) {return son[fa[p]][1]==p;}
  2. bool isroot(int p) {return son[fa[p]][0]!=p&&son[fa[p]][1]!=p;}
  3. void pushdown(int p)
  4. {
  5. if(!rev[p]) return;
  6. swap(son[son[p][0]][0],son[son[p][0]][1]);
  7. swap(son[son[p][1]][0],son[son[p][1]][1]);
  8. rev[son[p][0]]^=1,rev[son[p][1]]^=1,rev[p]=0;
  9. }
  10. void pushup(int p) {if(p) size[p]=1+size[son[p][1]]+size[son[p][0]];}
  11. void update(int p) {if(!isroot(p)) update(fa[p]);pushdown(p);}
  12. void rotate(int p)
  13. {
  14. int tmp1=fa[p],tmp2=fa[tmp1],tmp3=check(p);
  15. fa[son[tmp1][tmp3]=son[p][tmp3^1]]=tmp1,fa[p]=tmp2;
  16. if(!isroot(tmp1)) son[tmp2][check(tmp1)]=p;
  17. fa[son[p][tmp3^1]=tmp1]=p,pushup(tmp1),pushup(p);
  18. }
  19. void splay(int p)
  20. {
  21. update(p);
  22. for(int i;i=fa[p],!isroot(p);rotate(p))
  23. if(!isroot(i)) rotate(check(p)==check(i)?i:p);
  24. }
  25. void access(int p) {for(int t=0;p;t=p,p=fa[p]) splay(p),son[p][1]=t,pushup(p);}
  26. void makeroot(int p) {access(p),splay(p),swap(son[p][0],son[p][1]),rev[p]^=1;}
  27. void link(int x,int y) {makeroot(x),makeroot(y),fa[x]=y,ord[y]+=size[x];}
  28. void cut(int x,int y) {makeroot(x),access(y),splay(y),son[y][0]=fa[x]=0;}

Lct浅谈的更多相关文章

  1. 【转】Android Canvas的save(),saveLayer()和restore()浅谈

    Android Canvas的save(),saveLayer()和restore()浅谈 时间:2014-12-04 19:35:22      阅读:1445      评论:0      收藏: ...

  2. [随机化算法] 听天由命?浅谈Simulate Anneal模拟退火算法

    Simulate Anneal模拟退火算法,是一种用于得到最优解的随机化算法. 如果可以打一手漂亮的随机化搜索,也许当你面对一筹莫展的神仙题时就有一把趁手的兵器了. 这篇题解将教你什么?SA的基本思路 ...

  3. 浅谈 Fragment 生命周期

    版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Fragment 文中如有纰漏,欢迎大家留言指出. Fragment 是在 Android 3.0 中 ...

  4. 浅谈 LayoutInflater

    浅谈 LayoutInflater 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/View 文中如有纰漏,欢迎大家留言指出. 在 Android 的 ...

  5. 浅谈Java的throw与throws

    转载:http://blog.csdn.net/luoweifu/article/details/10721543 我进行了一些加工,不是本人原创但比原博主要更完善~ 浅谈Java异常 以前虽然知道一 ...

  6. 浅谈SQL注入风险 - 一个Login拿下Server

    前两天,带着学生们学习了简单的ASP.NET MVC,通过ADO.NET方式连接数据库,实现增删改查. 可能有一部分学生提前预习过,在我写登录SQL的时候,他们鄙视我说:“老师你这SQL有注入,随便都 ...

  7. 浅谈WebService的版本兼容性设计

    在现在大型的项目或者软件开发中,一般都会有很多种终端, PC端比如Winform.WebForm,移动端,比如各种Native客户端(iOS, Android, WP),Html5等,我们要满足以上所 ...

  8. 浅谈angular2+ionic2

    浅谈angular2+ionic2   前言: 不要用angular的语法去写angular2,有人说二者就像Java和JavaScript的区别.   1. 项目所用:angular2+ionic2 ...

  9. iOS开发之浅谈MVVM的架构设计与团队协作

    今天写这篇博客是想达到抛砖引玉的作用,想与大家交流一下思想,相互学习,博文中有不足之处还望大家批评指正.本篇博客的内容沿袭以往博客的风格,也是以干货为主,偶尔扯扯咸蛋(哈哈~不好好工作又开始发表博客啦 ...

随机推荐

  1. 《Cracking the Coding Interview》——第4章:树和图——题目7

    2014-03-19 04:48 题目:最近公共父节点问题. 解法1:Naive算法,先对其高度,然后一层一层往上直到找到结果. 代码: // 4.7 Least Common Ancestor // ...

  2. APP测试用例要考虑的一些方面

    安装与卸载:●应用是否可以在IOS不同系统版本或android不同系统版本上安装(有的系统版本过低,应用不能适配)●软件安装后是否可以正常运行,安装后的文件夹及文件是否可以写到指定的目录里.●安装过程 ...

  3. Opencv3.4.5安装包

    这个资源是Opencv3.4.5安装包,包括Windows软件包,Android软件包,IOS软件包,还有opencv的源代码:需要的下载吧. 点击下载

  4. android studio 配置网络代理

    1.首先在vultr网站购买服务器. 然后使用shadowsocksR给服务器配置FQ,再在本地机器配置好shadowsocksR. 参考网址:https://github.com/getlanter ...

  5. php session访问限制

    登录 <?php // 启动会话 session_start(); // 注册登陆成功的 admin 变量,并赋值 true $_SESSION["admin"] = tru ...

  6. CentOS7 设置开机直接进入命令行界面

    上网查询centsos设置开机直接进入命令行界面的方法都说修改/etc/inittab文件,将文件中的“ :id:5:initdefault:”改为“ :id:3:initdefault:”,即将默认 ...

  7. HTML5_纯JS实现上传文件显示文件大小,文件信息,上传进度_不使用JS库

    前台 html <input type="file" id="_netLogo" onchange="fileSelected();" ...

  8. 洛谷 P4882 lty loves 96! 解题报告

    P4882 lty loves 96! 题目背景 众所周知,\(lty\)非常喜欢\(96\)这两个数字(想歪的现在马上面壁去),更甚于复读(人本复)! 题目描述 由于爱屋及乌,因此,\(lty\)对 ...

  9. Codeforces Round #352 (Div. 2) A

    A. Summer Camp time limit per test 1 second memory limit per test 256 megabytes input standard input ...

  10. Spring----01. 入门知识,IoC/DI

    1.spring两个最基本概念:依赖注入DI.面向切面AOP 2.spring通过上下文Application Context装配bean,实现方式的区别是如何加载它们的配置信息, ClassPath ...