\(emmm\)学\(lct\)有几天了,大概整理一下这东西的题单吧 (部分参考flashhu的博客)

基础操作

[洛谷P1501Tree II]

题意 给定一棵树,要求支持 链加,删边加边,链乘,询问链权值 四种操作。

Sol: 大概是 \(lct\) 上维护加和乘标记的板子题

[SHOI2014 三叉神经树]

题意 给定一棵 $ 3\times n$ 个节点的树,编号在 \(1\sim n\) 的节点有且仅有三个儿子,编号在\(n+1\sim 3\times n\)的节点没有儿子。节点的值只可能为 \(0\) 或 \(1\) 。编号在 \(n+1\sim 3\times n\) 的节点的值由输入确定,编号在 \(1\sim n\) 的节点的值为其三个儿子中 \(0\) 和 \(1\) 较多的那种。现在有 \(m\) 个操作,每次操作会改变某个编号在 \(n+1\sim 3\times n\) 的值,请你在每次操作结束后输出根节点的值。

Sol: \(emmm\)让我调到凌晨两点的一道题 设题目中要求的值为 \(a\) ,每个节点可以根据子节点值为 \(0\) 的个数分为 \(0\sim 3\) 四种情况,设当前这个新值为 \(val\) 。观察到每次将某个叶子结点从 $0 $ 变成 \(1\) 实际上就是将从这个叶子结点向上连续一段 \(val\) 为 \(2\) 的点区间修改 \(val\) 为 \(1\)。从 \(1\) 变成 \(0\) 实际上就是将一段 \(val\) 为 \(1\) 的修改为 \(2\) 。我们需要维护一个数据结构查询一条链上值不为 \(1/2\) 的最深的点是哪个,可以在 \(lct\) 上二分深度,然后打 \(tag\) 修改就好了。时间复杂度 \(O(n\log ^2n)\)。

维护连通性&双联通分量

[SDOI2008 洞穴勘测]

题意 要求维护一个森林,支持动态加边,删边,询问两点是否联通。

Sol: 如果没有删边,可以直接并查集做一下,加上删边就 \(findroot\) 随便判一下就好了。大概是个板子题。貌似可以并查集做但是复杂度不太对?

[AHOI2005 航线规划]

题意 给定 \(n\) 个点 \(m\) 条边的无向图。要求支持动态删边,询问两点间桥边个数。

Sol: 大概是 \(lct\) 维护边双的板子题,还是挺重要的。动态删边不好做,可以离线转化为动态加边。如果两点 \(u,v\) 已经联通,那么如果再加一条连接 \(u,v\) 的边就需要将 \((u,v)\) 这一条链缩成一个双联通分量了。具体是 维护一个并查集 \(f[i]\) 表示点 \(i\) 所在的双联通分量的代表点,所有对点 \(i\) 的操作实际上都应该对点 \(f[i]\) 进行操作。如果要缩 \((u,v)\) 这条链上的所有点,那么就先把这条路径 \(split\) 出来,然后给这个分量选一个代表点,不妨选作点 \(v\),然后暴力 \(dfs\) 当前 \(splay\),将所有点的 \(f[i]\) 指向 \(v\),这样就成功缩成了一个点。还有一点要注意的就是每次使用 \(fa[i]\) 实际上都应该使用 \(find(fa[i])\),\(fa[i]\) 表示 \(splay\) 上 \(i\) 的父亲,\(find(i)\) 表示查询点 \(i\) 所在连通分量的代表点即 \(f[i]\)。

暴力 \(dfs\) 就长这样:

void dfs(int a,int b){father[a]=b;if(ch[a][0]) dfs(ch[a][0],b);if(ch[a][1]) dfs(ch[a][1],b);
}

维护边权&生成树

[WC2006 水管局长]

题意 给定 \(n\) 个点 \(m\) 条边的有向图,要求支持动态删边,询问点 \(a\) 到点 \(b\) 的所有路径上,路径上最大边权的最小值。

Sol: 话说交这题的时候卡着某人了2333 维护边权的套路是将每条边拆成一个点,这个点的权值是边的权值,然后这个点分别连接这条边的两个端点。然后视情况将其他点的点权赋为 \(-inf/inf\) 。还是套路离线,删边转化成加边。不难发现询问最大边权最小值就是让动态维护一个最小生成树,否则一定能找到不劣解。问题就转变成了动态维护最小生成树。对于 \(splay\) 上每个点 \(x\) 维护 \(mx[x]\) 和 \(id[x]\) 表示 \(x\) 的实儿子中最大的点权和最大的点权是哪个点。尝试加边 \((x,y)\) 时,如果两点不连通那么直接加上,否则将 \((x,y)\) \(split\) 出来。一个显然的性质是点权最大的点一定是原图中的边。如果这颗 \(splay\) 中点权最大的点都要小于当前边的边权,那么跳过当前边,否则让点权最大的点所代表的边与这条边的两个端点 \(cut\) 出来,再将待加入边和两个端点 \(link\) 起来就好了。

[NOI2014 魔法森林]

题意 每条边有两个权值 \(a_i,b_i\),要求一条从 \(1\) 到 \(n\) 的路径,使得 \(\max \left\{a\right\}+\max\left\{b\right\}\) 最小。求出这个最小值。

Sol: 做这道题的上午的数学课上 \(van\) 老师 (数学老师) 刚好讲到了求无限制二元函数最值时的思想,就是"固定一个求另一个"。然后自然而然的想到了这道题上,我们可以"固定" \(a\) 求 \(b\) 的最小生成树,再详细点就是枚举路径上 \(a\) 的最大值 \(x\) ,然后将所有 \(a_i<x\) 的边 \(i\) 以 \(b_i\) 为关键字做最小生成树,就可以用当前的 \(x+\max\left\{b\right\}\) 去更新答案了。观察到不用每次都重新求一遍最小生成树,只需要用 \(lct\) 维护以 \(b\) 为关键字的最小生成树,每次尝试加边就行了。

维护子树信息

\(lct\) 不是很擅长维护子树信息,因为 \(splay\) 上的儿子不一定就是原图上的儿子但是也不是不能做

以维护子树 \(size\) 举例。我们设 \(s[x]\) 表示 \(x\) 的所有虚子树(通过虚边指向\(x\))的 \(size\) 和,\(sze[x]\) 表示 \(lct\) 上点 \(x\) 的所有儿子的 \(size\) 和(包括 \(Splay\) 中相对的左右儿子的总和与被轻边所指的虚子树的总和)。那么 \(pushup\) 就是这么写:

void pushup(int x){sze[x]=sze[ch[x][0]]+sze[ch[x][1]]+s[x]+1
}

考虑如何维护 \(s[x]\)

假设当前已经维护好了 \(s[x]\),考虑 \(lct\) 的哪些操作会改变 \(s[x]\) 的值

\(\dots\)

经过前人分析,只有 \(access\) 和 \(link\) 操作会改变 \(s[x]\) 的值。

具体是:

inline void access(int x){for(int y=0;x;y=x,x=fa[x]){//x将要失去一个右儿子再得到一个右儿子,所以要加上失去的减去得到的(因为是虚子树)splay(x);sze[x]+=s[ch[x][1]];sze[x]-=s[ch[x][1]=y];pushup(x);}
}
//保证连边合法
inline void link(int x,int y){split(x,y);//这里不是提取x-y的路径的意思,是makeroot+access+splay的偷懒写法fa[x]=y;s[y]+=sze[x];pushup(y);
}

尤其要注意 \(link\) 里加上的是 \(sze\) 而不是 \(s\)。

[BJOI2014 大融合]

题意 要求支持动态连边,询问有多少点对间的路径经过边 \((x,y)\)。

Sol: 维护子树信息的板子题,按上边讲的直接做就好了。

[洛谷U19464 山村游历]

题意 有点复杂...还是去看原题吧

Sol: 懒得写...去看这篇题解吧 戳我戳我戳我戳我戳我!!! 不过这题姿势是真的高

[洛谷P4299 首都]

题意 要求维护一个森林,支持动态加边(保证合法),询问某点所在树的重心,询问所有树的重心的异或和。

Sol: 对着题解调的一道题...

首先有两个关于树的重心的性质:

  1. 如果两棵树连在了一起,新的重心一定是原来两个重心路径上的某点
  2. 如果一棵树增加一个叶子结点,那么重心最多移动一次(即重心最多只会移动到相邻的点上)

有了这两个性质就可以做这题了。每次连边 \((x,y)\) 的时候启发式合并一下,将 \(size\) 小的那个树上的每个节点拎出来扔进 \(size\) 多的树上,然后维护重心的移动就行了。每个点最多被拎出来 \(\log n\) 次,总复杂度 \(O(n\log ^2n)\)。

维护重心的移动说的有点简单,再详细说说。因为性质 \(2\) ,所以每次加一个点时判断重心是否会在重心与新加的点的路径上移动一步就行了。再具体点假设原来的重心为 \(rt\),新加入的点为 \(y\),首先要先求出重心可能会变成哪个点。先 \(makeroot(rt),access(y),splay(rt)\),这就保证了 \(rt\) 到 \(y\) 的路径都在一棵 \(splay\) 里。然后一直找 \(ch[rt][1]\) 的左儿子,即 \(splay\) 上 \(rt\) 的后继 \(now\) ,这就是有可能成为新的重心的点。然后由重心的性质:不存在某个儿子的 \(size\) 的 \(2\) 倍大于整棵树的总点数,判断一下 \(sze[now]\times 2\) 和 \(sze[rt]\) 的大小就好了。

[SPOJ2939 QTREEV]

题意 给定一棵 \(n\) 个点的树,每个点有可能是黑色或白色,一开始都是黑的。要求支持 反转某点颜色 询问离点 \(x\) 到最近的白点的距离

Sol: 一群神仙网友都觉得这是道傻逼题于是写的很不详细只有我傻逼地想了整个上午下午 动态点分治似乎可以轻松切掉?为了练 \(lct\) 来写这道题没想太多。

维护子树信息的 \(lct\),但是不是简单的维护子树大小而是维护一个类似子树最值的东西,这个可以在每个点开个 \(multiset\) 随便维护下。

难的是状态的定义。我们定义 \(lmn[x],rmn[x]\) 分别代表在 \(splay\) 中 \(x\) 的子树里深度最浅的点能够到达最近的白点的距离和深度最深的点能够到达最近的白点的距离。这里说是“最浅”和“最深”是因为 \(splay\) 中每棵子树实际上都是原图(实际上是原树)中一条深度单调递增的链。

有了这个状态就可以方便的 \(pushup\) 更新父节点的信息了,这里建议在纸上画下一条链然后把它建成 \(splay\) ,对着这棵 \(splay\) 可以很直观地写出 \(pushup\) 函数。

我的代码里全程没有用到 \(makeroot\),因为如果 \(makeroot\) 的话就需要翻转左右子树,而点 \(x\) 的信息是跟左右子树的顺序有关的(这个跟平常那些维护子树大小的有很大区别),所以大概需要在 \(pushup\) 里面套上 \(pushdown\), \(pushdown\) 里面再套上 \(pushup\) 很是麻烦还会TLE索性就不去写了。

做完这题还可以去做 \(Qtree\;4\) 是这题的升级版 然而我做这题时网上一个像样的题解都没有于是乎心力交瘁痛不欲生就不想再去刚那题了

维护树上同色联通块

维护方法视情况而定

可以通过一个 \(lct\) 将所有的点串起来,其中一个 \(splay\) 里维护同色的点

还可以有多少颜色开多少 \(lct\),然后两点同色就在对应 \(lct\) 中连上边

[SDOI2017树点涂色]

题意 给定一棵以 \(1\) 号点为根的有根树,每个点都有一个颜色,最开始点上的颜色两两不同。定义一条路径的权值为这条路径上的点不同的颜色个数。要求支持以下三个操作:

  1. 将点 \(x\) 到根节点的路径上所有的点染上一种没有用过的新颜色
  2. 询问 \(x\) 到 \(y\) 路径权值
  3. 询问从 \(x\) 的子树中选一个点到根节点的路径最大的权值

Sol: 感觉 \(1\) 操作特别像 \(lct\) 的 \(access\),就是把 \(1\) 到 \(x\) 这条链放进一个 \(splay\) 里,然后就想到了维护一个 \(lct\),每个 \(splay\) 都保存着颜色相同的一条链(因为颜色相同的所有点必然在一条深度递增的链上)。

考虑如何回答 \(2\) 操作。可以弄一个 \(f[i]\) 表示从 \(1\) 到 \(i\) 的路径的权值。然后答案就是 \(f[x]+f[y]-2*f[lca(x,y)]+1\) 了。这个可以自己画一下图还是比较显然的。

怎么维护好这个 \(f\) 呢?

考虑 \(access\) 的过程,就是不断将一条实边断为虚边,再将一条虚边连成实边。而这个 \(f\) 数组本质上就是从 \(1\) 到 \(i\) 经过的虚边个数(或许要加上 \(1\) ),那我们在 \(access\) 每次断边的时候动态维护好这个 \(f\) 就好了。

具体是因为要断开 \(x\) 和 \(ch[x][1]\) 之间的实边将其变为虚边,设在 \(splay\) 中 \(ch[x][1]\) 的子树中最靠左(即在原树中深度最小的点)的点为 \(z\),那么我们就需要将 \(z\) 的子树中所有点的 \(f\) 值 \(+1\) (因为多了一条从 \(x\) 连向 \(z\) 的虚边),然后还要连上 \(x\) 到 \(y\) 的实边,就需要将 \(splay\) 中 \(y\) 的子树中最靠左的点的子树中所有点的 \(f\) 值 \(-1\)。

用 \(dfs\) 序+线段树维护就资瓷区间加减区间查询了。

[SPOJ16549 QTREEVI]

题意 给定一棵 \(n\) 个点的树,每个点的颜色可以为白色或黑色 。要求支持两个操作:

  1. 改变点 \(x\) 的颜色
  2. 询问包含点 \(x\) 的同色联通块的大小

Sol: 首先如果熟练的话会想到一个做法就是对于两个颜色分别维护一个 \(lct\),修改颜色的时候暴力 \(link/cut\) 与这个点相连的所有点。显然可以被菊花图卡成 \(n^2\) 。

许多与树有关的题目,边权不好处理时,常将边权下放为点权。这题我们可将点权上推到边权。

同样对于两个颜色分别维护 \(lct\),分别叫做黑 \(lct\) 和白 \(lct\)。

把每个点的父边赋予该点的颜色,如果一条边 \(<fa,x>\) 在黑 \(lct\) 中出现,那么点 \(x\) 的颜色一定是黑色的,白色同理。

发现这样做就可以快速修改了。

那能不能支持查询呢? 废话

如果要查询点 \(x\),那我们可以在对应颜色的 \(lct\) 中找到 \(x\) 所在的树的根节点 \(rt\) ,即 \(rt=findroot(x)\)。然后答案就是 \(sze[ch[rt][1]]\)。不是 \(sze[rt]\) 的原因是 \(<fa[rt],rt>\) 这条边一定没有在当前 \(lct\) 中出现不然根节点就是 \(fa[rt]\) 了。既然没有出现那就代表 \(rt\) 的颜色和 \(x\) 的颜色一定不一样,所以答案是 \(rt\) 下面那个点的子树大小,不然会通过 \(rt\) 连接到别的子树导致答案变大。

[SPOJ16580 QTREEVII]

题意 给定一棵 \(n\) 个点的树,每个点的颜色可以为白色或黑色,同时每个点还有点权 。要求支持两个操作:

  1. 询问包含点 \(x\) 的同色联通块中最大的点权
  2. 改变点 \(x\) 的颜色
  3. 修改点 \(x\) 的点权

Sol: 会了上面那题的话这题的解法就能很自然地出来了。就是加了一个子树最值,随便拿 \(multiset\) 维护下就好。


涉及 \(lct\) 基础套路的东西大概就这么多吧,一些特别好的题会单独拎出来写。

转载于:https://www.cnblogs.com/YoungNeal/p/10009546.html

[总结] LCT学习笔记相关推荐

  1. LCT学习笔记/基本思路

    我仅一届平凡人 LCT(Link Cut Tree)可以看作是Splay树维护的树链剖分.我们可以在一个动态树(森林)上维护树链信息. 重链剖分与实链剖分 类比树链剖分的重链剖分: 在Splay树的基 ...

  2. Link Cut Tree 学习笔记

    Link Cut Tree 学习笔记 说在前边 最近补 CF 碰见一道 LCT ,就打算学习一下这个东西...顺便复习一下 splay. 具体算法及实现 参考了FlashHu, Candy? P369 ...

  3. python半径为3圆形区域边界曲线_OpenCV 学习笔记03 边界框、最小矩形区域和最小闭圆的轮廓...

    本节代码使用的opencv-python 4.0.1,numpy 1.15.4 + mkl 使用图片为 Mjolnir_Round_Car_Magnet_300x300.jpg 代码如下: impor ...

  4. 红帽认证学习笔记-破解密码

    红帽认证学习笔记-破解密码 1. 启动的时候,在启动界面,相应启动项,内核名称上按"e": 2. 进入后,找到linux16开头的地方,按"end"键到最后,输 ...

  5. PyTorch 学习笔记(六):PyTorch hook 和关于 PyTorch backward 过程的理解 call

    您的位置 首页 PyTorch 学习笔记系列 PyTorch 学习笔记(六):PyTorch hook 和关于 PyTorch backward 过程的理解 发布: 2017年8月4日 7,195阅读 ...

  6. 容器云原生DevOps学习笔记——第三期:从零搭建CI/CD系统标准化交付流程

    暑期实习期间,所在的技术中台-效能研发团队规划设计并结合公司开源协同实现符合DevOps理念的研发工具平台,实现研发过程自动化.标准化: 实习期间对DevOps的理解一直懵懵懂懂,最近观看了阿里专家带 ...

  7. 容器云原生DevOps学习笔记——第二期:如何快速高质量的应用容器化迁移

    暑期实习期间,所在的技术中台-效能研发团队规划设计并结合公司开源协同实现符合DevOps理念的研发工具平台,实现研发过程自动化.标准化: 实习期间对DevOps的理解一直懵懵懂懂,最近观看了阿里专家带 ...

  8. 2020年Yann Lecun深度学习笔记(下)

    2020年Yann Lecun深度学习笔记(下)

  9. 2020年Yann Lecun深度学习笔记(上)

    2020年Yann Lecun深度学习笔记(上)

  10. 知识图谱学习笔记(1)

    知识图谱学习笔记第一部分,包含RDF介绍,以及Jena RDF API使用 知识图谱的基石:RDF RDF(Resource Description Framework),即资源描述框架,其本质是一个 ...

最新文章

  1. 到底有多火?三家单位争抢发布,谷歌、清华、牛津同时提超越注意力的新机制...
  2. Linux Kernel TCP/IP Stack — L3 Layer — netfilter 框架 — conntrack(CT,连接跟踪)
  3. linux vim模板,FreeBSD8下的vim配置模板
  4. 数据挖掘导论读书笔记4--其他分类技术
  5. 【网络编程】之九、事件选择WSAEventSelect
  6. 使用DIV之后 table何去何从
  7. WinCE的I2C驱动程序设计
  8. shell 脚本编程的10 个最佳实践
  9. C++实现神经网络之一 | Net类的设计和神经网络的初始化
  10. 最新西安计算机信息会议,第23届全国信息存储技术学术会议在西安召开
  11. 2017.9.17 function 思考记录
  12. python测开课程_2020年第五期《python接口自动化+测试开发》课程,10月11号开学(火热报名中!)...
  13. Node-red初级入门
  14. 天翼网盘在线直链解析源码
  15. 关于三栏式布局的几种方式
  16. 对比学习的应用(SimCSE,CLEAR,DeCLUTR,DiffCSE)
  17. 基于飞桨本地ocr安卓按键插件
  18. CAD 批量打印,输出pdf,plt的工具
  19. 【设计模式11】备忘录和解释器模式 SpelExpressionParser
  20. 实用帖!推荐一个无版权、免费、高清图片素材网站!

热门文章

  1. 在 Mac 上的“照片”中如何把文件夹中的相簿分组?
  2. Mac如何为应用单独设置语言?
  3. AweEraser for Mac如何永久删除笔记本电脑上的数据?
  4. cordova 打包vue 集成的app , router-view 默认首页白屏
  5. Stack Overflow首席大神,他回答了超过3万个问题
  6. javascript的array.some()和array.every()
  7. C#将word转换为HTML格式
  8. Swift - 设置应用程序图标的提醒个数(右上角小红圈)
  9. 芒果 TV Redis 服务解决方案
  10. Python使用总结