模板:Link Cut Tree(LCT)

文章目录

  • 前言
  • 解析
    • 原理
    • rotate(x)
    • splay(x)
    • access(x)
    • findroot(x)
    • makeroot(x)
    • split(x,y)
    • link(x,y)
    • cut(x,y)
    • pushdown(x)
  • 完整代码

所谓Link Cut Tree,就是林可卡特发明的tree

(逃)

前言

终于走到了这一天…
其实感觉没有预想的那么难(单纯就学习算法和理解模板而言)
至少感觉比学平衡树轻松

LCT是一种很强大的树形数据结构
最重要的功能优势就是支持单log的复杂度完成加边、删边、换根的操作
在路径问题上十分强大

解析

原理

LCT的核心原理是实链剖分
和重链剖分类似的,将一棵树中的边分为实边和虚边
每个结点的最多有一条连向儿子的实边(与树链剖分不同的,也可以没有)

把实边连成的链成为实链
把在同一条实链上的结点放到同一个splay中
这个splay的中序遍历就是这条链从上到下的顺序
这样就间接的表示了所有实边的信息

那么如何表示虚边?
假设原树上有一条 fafafa 指向sonsonson的虚边
就把son所在的splay的根节点指向fa,从而表示出虚边
根节点也有父亲了,如何判断根节点呢?
只有该结点的父亲的左右儿子都不是自己,那么这个结点就是根节点

inline bool isroot(int x) {return tr[f[x]][0]!=x&&tr[f[x]][1]!=x;
}

下面我们来讲讲具体的操作函数

rotate(x)

和普通的splay大同小异,唯一需要注意的是有一句

if(gfa) tr[gfa][which(fa)]=x;

变成了:

if(!isroot(fa)) tr[gfa][which(fa)]=x;

较好理解,因为判定根的条件变了

splay(x)

将x转到所在splay的根节点,由于只需要转到根一个功能,穿一个参即可
注意:不同于正常的splay,需要先把链上的标记下放
实现有两种方法
第一种是递归:

void pushall(int x){//printf("%d\n",x);if(!isroot(x)) pushall(f[x]);pushdown(x);
}

第二种是手写栈:

int y=x,top=0;zhan[++top]=y;while(!isroot(y)) zhan[++top]=y=f[y];

建议使用第二种,因为第一种不小心容易写完函数后忘记调用(别问我为什么知道
然后和rotate类似的,对根的判断略有区别
不过都是一个道理

inline void splay(int x) {int y=x,top=0;zhan[++top]=y;while(!isroot(y)) zhan[++top]=y=f[y];while(top) pushdown(zhan[top--]);for(int fa; fa=f[x],!isroot(x); rotate(x)) {if(!isroot(fa)) which(fa)==which(x)?rotate(fa):rotate(x);}return;
}

access(x)

重中之重,LCT的灵魂

功能:提取出 x 到根节点的一条实链

先把x的尾巴去掉,具体来说就是转到根,然后砍掉右子树
然后一路往上直至 fa 指针为空,过程中需要改变实虚边的状态

inline void access(int x) {for(int y(0); x; y=x,x=f[x]) {splay(x);tr[x][1]=y;pushup(x);if(y) f[y]=x;}return;
}

findroot(x)

功能:找到x所在的树的根节点(注意是真实的树而不是splay的根)

先access(x),然后把x转到根,不断往左走即可

inline int findroot(int x) {access(x);splay(x);while(pushdown(x),tr[x][0]) x=tr[x][0];splay(x);return x;
}

makeroot(x)

功能:把x作为其所在树的根(换根)
前面说过,父子关系在splay中的体现是中序遍历
所以换根(即颠倒x-rt路径上所有的父子关系),其实就是区间翻转
先access(x),然后转到根,然后用类似文艺平衡树的方式在x上打一个翻转标记即可

inline void makeroot(int x) {access(x);splay(x);tag(x);return;
}

(最愉快的代码)

split(x,y)

功能:提取出以x、y为两端点的实链(前提是二者联通)

makeroot(x)之后access(y)即可
为了后面方便,常常再加一步splay(y)(不然连根是谁都不知道提取个寂寞

inline void split(int x,int y){//y is fathermakeroot(x);access(y);splay(y);return;
}

link(x,y)

功能:加一条边(x,y)(x,y)(x,y)(在有出现环的时候忽略操作)

出现环(即xy原来就联通)可以用findroot判掉
否则,先makeroot(x),然后把f(x)指向y即可
记得按需要更新y的信息!

inline void link(int x,int y) {makeroot(x);if(findroot(y)==x) return;f[x]=y;pushup(y);//printf("link: %d -> %d\n",x,y);
}

cut(x,y)

功能:切断边(x,y)(x,y)(x,y)(在没有这条边时忽略操作)

先makeroot(x),然后access(y),splay(y)到根
此时如果存在这条边,必然x和y是在同一个splay中
判断的充要条件:x是y的左儿子且x没有右儿子,比较显然
如果存在的话,把这条y的左儿子和x的父亲赋值为0即可
不要忘记更新y的信息(又一次)

inline void cut(int x,int y) {makeroot(x);access(y);splay(y);if(tr[y][0]!=x||tr[x][1]) return;tr[y][0]=f[x]=0;pushup(y);return;
}

pushdown(x)

大部分时候只需要翻转
一个看到的地方都说比较“稳妥”的写法
(谁不喜欢稳妥呢)

inline void tag(int x) {if(x) {rev[x]^=1;swap(tr[x][0],tr[x][1]);}
}
inline void pushdown(int x) {if(rev[x]){rev[x]=0;tag(tr[x][0]);tag(tr[x][1]);}return;
}

完整代码

经验传送门

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=2e5+100;
const int mod=1e9+7;
const double eps=1e-9;
inline ll read() {ll x(0),f(1);char c=getchar();while(!isdigit(c)) {if(c=='-')f=-1;c=getchar();}while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}return x*f;
}int n,m;#define ls(o) tr[o][0]
#define rs(o) tr[o][1]
int tr[N][2],f[N],rev[N],val[N],mx[N];
inline bool isroot(int x) {return tr[f[x]][0]!=x&&tr[f[x]][1]!=x;
}
inline bool which(int x) {return tr[f[x]][1]==x;
}
inline void pushup(int x) {if(x){mx[x]=x;if(ls(x)&&val[mx[ls(x)]]>val[mx[x]]) mx[x]=mx[ls(x)];if(rs(x)&&val[mx[rs(x)]]>val[mx[x]]) mx[x]=mx[rs(x)];}return;
}
inline void tag(int x) {if(x) {rev[x]^=1;swap(tr[x][0],tr[x][1]);}
}
inline void pushdown(int x) {if(rev[x]){rev[x]=0;tag(tr[x][0]);tag(tr[x][1]);}return;
}
void debug(int x) {if(!x) return;pushdown(x);debug(tr[x][0]);printf("debug: x=%d ls=%d rs=%d\n",x,tr[x][0],tr[x][1]);debug(tr[x][1]);return;
}
inline void rotate(int x) {int fa=f[x],gfa=f[fa];int d=which(x),son=tr[x][d^1];f[x]=gfa;if(!isroot(fa)) tr[gfa][which(fa)]=x;f[fa]=x;tr[x][d^1]=fa;if(son){f[son]=fa;}tr[fa][d]=son;pushup(fa);pushup(x);return;
}
int zhan[N];
inline void splay(int x) {int y=x,top=0;zhan[++top]=y;while(!isroot(y)) zhan[++top]=y=f[y];while(top) pushdown(zhan[top--]);for(int fa; fa=f[x],!isroot(x); rotate(x)) {if(!isroot(fa)) which(fa)==which(x)?rotate(fa):rotate(x);}return;
}
inline void access(int x) {for(int y(0); x; y=x,x=f[x]) {splay(x);tr[x][1]=y;pushup(x);if(y) f[y]=x;}return;
}
inline void makeroot(int x) {access(x);splay(x);tag(x);return;
}
inline int findroot(int x) {access(x);splay(x);while(pushdown(x),tr[x][0]) x=tr[x][0];splay(x);return x;
}
inline void link(int x,int y) {makeroot(x);if(findroot(y)==x) return;f[x]=y;pushup(y);//printf("link: %d -> %d\n",x,y);
}
inline void cut(int x,int y) {makeroot(x);access(y);splay(y);if(tr[y][0]!=x||tr[x][1]) return;tr[y][0]=f[x]=0;pushup(y);return;
}
inline void split(int x,int y){//y is fathermakeroot(x);access(y);splay(y);return;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/317362.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

P6803-[CEOI2020]星际迷航【博弈论,dp,矩阵乘法】

正题 题目链接:https://www.luogu.com.cn/problem/P6803 题目大意 给出一棵nnn个点的树&#xff0c;把它复制出D1D1D1层&#xff0c;编号为[0,D][0,D][0,D]&#xff0c;然后每一层随机一个点向下一层随机一个点连边。 然后从第000层的111号点出发&#xff0c;两个人轮流操作…

潘淳:国士无双《微软技术俱乐部(苏州)成立大会暨微软技术交流会介绍》附专题视频...

题记&#xff1a;凡我赶不上的&#xff0c;我就在未来等他&#xff0c;随时等待捕捉那个趋势的的到来。2019年立一个FLAG&#xff0c;加入一个成长性组织&#xff0c;一个能让我学习使我成长的平台。2019年你和我都将亲历这个组织的到来&#xff0c;一个属于苏州程序员自己的大…

ASP.NET Core中借助CSRedis实现安全高效的分布式锁

引言最近回头看了看开发的.NET Core 2.1项目的复盘总结&#xff0c;其中在多处用到Redis实现的分布式锁&#xff0c;虽然在OnResultExecuting方法中做了防止死锁的处理&#xff0c;但在某些场景下还是会发生死锁的问题&#xff0c;下面我只展示部分代码&#xff1a;问题&#x…

L Machining Disc Rotors

L Machining Disc Rotors 题意&#xff1a; 圆心为(0,0)半径为R的圆&#xff0c;现在被被n个互不相交的圆切割(圆心和半径会给出)&#xff0c;保证这n个彼此之间不会交叉&#xff0c;保证n个圆中不会有某个包含整个大圆的情况。问切割后大圆剩余部分的直径&#xff08;即两点…

线性代数二之矩阵加速DP——数学作业,Arc of Dream

矩阵加速数学作业descriptionsolutioncodeArc of Dreamdescriptionsolutioncode数学作业 description solution dpdpdp状态转移方程&#xff0c;dpidpi−1∗10lenii(modM)dp_{i}dp_{i-1}*10^{len_i}i\pmod Mdpi​dpi−1​∗10leni​i(modM) nnn巨大&#xff0c;分段矩阵加速 …

pjudge#21655-[PR #5]双向奔赴【状压dp】

正题 题目链接:http://pjudge.ac/contest/951/problem/21655 题目大意 给出一张nnn个点的简单无向图&#xff0c;每条边的两个方向具有不同权值。求一个权值和最小的定向方案使得整张图强连通。 1≤n≤18,−1≤ai,j≤1061\leq n\leq 18,-1\leq a_{i,j}\leq 10^61≤n≤18,−1≤…

YBTOJ洛谷P2387: 魔法森林(LCT)

解析 LCT从板子到算法的入门题吧 有一些不知道的很实用的技巧 把边按a排序从小到大加入边 那么我们只需要维护当前1-n路径上的b的最小值即可 如果这条边两端点本来不连通&#xff0c;就直接link 否则找到路径上b最大的一条边&#xff0c;断掉&#xff0c;再加入当前边&#x…

Shadow Properties之美(二)【Microsoft Entity Framework Core随笔】

接着上一篇Shadow Properties之美&#xff08;一&#xff09;&#xff0c;我们来继续举一个有点啰嗦的栗子。先看简单需求&#xff1a;某HR系统&#xff0c;需要记录员工资料。需要记录的资料有&#xff1a;员工号&#xff08;规则&#xff1a;分公司所在城市拼音首字母&#x…

K - Let the Flames Begin

K - Let the Flames Begin 题意&#xff1a; n个人围成一个环&#xff0c;编号分别是1~n&#xff0c;从第一个人开始报数&#xff0c;报道k的人被移除&#xff0c;然后下一个人从1重新报&#xff0c;一直这样进行。问第m给被移除的人报数是多少&#xff1f; 一共T组数据&…

CF1276F-Asterisk Substrings【SAM,线段树合并】

正题 题目链接:https://www.luogu.com.cn/problem/CF1276F 题目大意 给出一个长度为nnn的字符串SSS&#xff0c;现在依次进行如下操作 取出SSS的一个子串TTT。将TTT中的一个字符替换成∗*∗号&#xff08;也可以不替换&#xff09; 求最后有多少种不同的TTT。 解题思路 发…

线性代数三之状压DP的矩阵加速——Quad Tiling,Bus公交线路

状压与矩阵加速的藕断丝连Quad Tilingdescriptionsolutioncode[Hnoi2010]Bus 公交线路descriptionsolutioncodeQuad Tiling description solution 设dpi,S:dp_{i,S}:dpi,S​: iii列的状态为SSS的方案数&#xff0c;最后答案为dpn,(1<<4)−1dp_{n,(1<<4)-1}dpn,(…

codeforces:812(div2):总结

前言 比较水的一场比赛 E题几乎是一本通原题而我还是不会做qwq A - Sagheer and Crossroads 有一个十字路口&#xff0c;给出四个路口的车是否可以左转/右转/直行&#xff0c;并且给出每个路口的行人是否可以通过&#xff0c;求是否出现车和人冲突的情况 阅读理解题&#xff…

如何在ASP.NET Core程序启动时运行异步任务(2)

原文&#xff1a;Running async tasks on app startup in ASP.NET Core (Part 2)作者&#xff1a;Andrew Lock译者&#xff1a;Lamond Lu在我的上一篇博客中&#xff0c;我介绍了如何在ASP.NET Core应用程序启动时运行一些一次性异步任务。本篇博客将继续讨论上一篇的内容&…

Gym 102798A Golden Spirit

VJ链接 题意&#xff1a; 河的两岸各有n个人&#xff0c;中间有个桥&#xff0c;过桥时间为t&#xff0c;所有人过桥后要休息x时间&#xff0c;你每次可以带一个人过桥&#xff08;每次最多只能带一个人&#xff09;&#xff0c;问将所有人带到对岸并带回来&#xff0c;最短需…

P8347-「Wdoi-6」另一侧的月【博弈论,结论】

正题 题目链接:https://www.luogu.com.cn/problem/P8347 题目大意 给出一棵树&#xff0c;两个人轮流操作。 操作者可以选择一个点删除&#xff0c;然后选择一个剩下的连通块&#xff0c;删除其他连通块。 操作完成后只剩下一个点的人失败&#xff0c;求是否先手必败。 1≤…

线性代数四之动态DP(广义矩阵加速)——Can you answer these queries III,保卫王国

动态DP——广义矩阵加速SP1716 GSS3 - Can you answer these queries IIIdescriptionsolutioncode[NOIP2018 提高组] 保卫王国descriptionsolutioncode动态DP能矩阵加速要满足外层操作符对内层操作符具有分配率加法对于乘法就具有分配率(ab)*ca*cb*c SP1716 GSS3 - Can you a…

洛谷P4219 大融合(LCT、虚子树)

解析 本题需要用LCT维护子树大小 然后我就不会了 然后我就用树剖水过去了 又快又好写&#xff0c;真香 现在详细聊聊如何用LCT维护子树信息 每个结点再定义一个新的变量记录所有虚儿子的信息 然后…完了&#xff1f; 告别盲目pushup&#xff0c;我们来详细聊聊在哪里需要更新…

.Net Core跨平台应用研究-HelloArm(串口篇)

引言为了验证采用dotnet core技术开发的物联网设备数据采集接入服务应用是否能在高性价比的linux嵌入式平台运行&#xff0c;针对dotnet core应用程序进行嵌入式linux环境的发布部署运行验证研究。硬件环境硬件系统经过对比筛选&#xff0c;选用了友善之臂出品的NanoPC-T3 Plus…

H - Message Bomb Gym - 102798H

H - Message Bomb Gym - 102798H 题意&#xff1a; 有n个团队&#xff0c;m个人&#xff0c;s个操作 操作1&#xff1a;学生x加入y团队 操作2&#xff1a;学生x推出y团队 操作3&#xff1a;学生x在团队y发送一个信号&#xff0c;在团队y内的所有成员&#xff08;除了x&#x…

Loj#3005-「JOISC 2015 Day 4」Limited Memory【交互题】

正题 题目链接:https://loj.ac/p/3005 题目大意 有一个长度为nnn的括号串SSS&#xff0c;其中包括[]和<>两种括号类型&#xff0c;一个合法的括号串要求同类型的括号一一对应。 你每次可以询问SSS中的一个字符并且传递一个[0,222)[0,2^{22})[0,222)的数字到下一次。 …