介绍、用途

LCT是树链剖分中的一种,又叫实链剖分、动态树,常用于维护动态的树、森林。

维护方式

LCT并不直接维护原树,而是用一堆splay作为辅助树来维护。原树中的一条实链上的点在一棵splay中,虚边体现为辅助上的连接两棵splay的虚边,只认爸爸不认儿子。

变量介绍

 int n,m;
struct Node {
int fa,son[]; //爸爸、儿子(0左1右)
int val,all; //该点权值、子树异或和
char ifz; //是否翻转(0否1是)
void res() { //重置(然并卵)
fa=son[]=son[]=val=;
}
} tree[maxn];
int pre[maxn],inp; //翻转序列(splay用)

var

各种操作

判断一个点是哪个儿子

不多说了

 char which(int x) {
return x==tree[tree[x].fa].son[];
}

which

判断一个点是不是该splay的根

也不多说了

 char isroot(int x) {
return x!=tree[tree[x].fa].son[which(x)];
}

isroot

splay的操作

 void rotate(int x) {
int f=tree[x].fa,ff=tree[f].fa,c=which(x);
if(!isroot(f)) tree[ff].son[which(f)]=x; //若它爸是根就不要搞它爷了
tree[x].fa=ff;
tree[f].son[c]=tree[x].son[c^];
tree[tree[f].son[c]].fa=f;
tree[x].son[c^]=f;
tree[f].fa=x;
update(f);
update(x);
}
void splay(int x) {
int f;
pre[inp=]=x;
for(f=x; !isroot(f); f=tree[f].fa) pre[++inp]=tree[f].fa; //挖出它到根的点
fdi(i,inp,,) pushdown(pre[i]); //全部pushdown
for(; !isroot(x); rotate(x))if(!isroot(tree[x].fa))rotate((which(tree[x].fa)^which(x))?x:tree[x].fa); //无需pushdown
}

splay

打通这个点到原树的根为实链

这个是重点!!!是LCT的核心!!!

首先,先将该节点splay到根,并将其爸爸splay到根。于是我们知道,它爸爸的右儿子深度大于它爸爸,是需要砍成虚边的点,而它的深度也大于它爸爸,所以直接将它爸爸的右儿子变成它。重复上述操作,直到它无爸爸。

 void access(int x) {
for(int pr=; x; pr=x,x=tree[x].fa)splay(x),tree[x].son[]=pr,update(x);
}

access

将这个点变成原树的根

先打通这个点到根,并将它splay到根。然后我们可以发现,不在这棵splay上的点不受影响,而这棵splay上的点深浅颠倒,对应到splay上就是区间翻转。所以给它打上一个翻转标记。

 void makeroot(int x) {
access(x);
splay(x);
tree[x].ifz^=; //打翻转标记
}

makeroot

查找这个点所在原树的根

先打通它到根并splay,然后找到它所在splay的最左边的点(即一直往左儿子找)。

 int find(int x) {
for(access(x),splay(x); tree[x].son[]; x=tree[x].son[]);
return x;
}

find

连接个点并连接两棵树

将一个点变成根,并令这个点爸爸为另一个点。注意先判断这两个点在不在一棵树内,在就不用连了。

 void link(int x,int y) {
makeroot(x);
tree[x].fa=y;
}

link

切断两点之间的边

先判断在不在一棵树内,不在就不切。然后将一个点变成根,另一个点打通到根并splay到根。易发现若这两个点间有边则这棵splay中只有它们俩。判断一下即可。

 void cut(int x,int y) {
makeroot(x);
access(y);
splay(y);
if(tree[y].son[]==x&&!tree[y].son[]&&!tree[x].son[]&&!tree[x].son[])tree[y].son[]=tree[x].fa=;
}

cut

改变一个点的值

将这个点变成根,并将其splay,再改变权值即可。

 void change(int x,int y) {
makeroot(x);
splay(x);
tree[x].val=y;
update(x);
}

change

查询x到y的异或和

将x变成根,打通y并splay,直接查询即可。

 int query(int x,int y) {
makeroot(x);
access(y);
splay(y);
return tree[y].all;
}

query

时空复杂度

时间复杂度

splay:均摊O(logn)的不用说了吧

access:由于每次access最多有logn条实边变成虚边,splay复杂度也仅为均摊O(logn),因此时间复杂度均摊O(logn)

makeroot:makeroot的开销主要为access,因此也为均摊O(logn)

其他:基于以上三种操作,因此都为均摊O(logn)

只是常数无比巨大!!!
只是常数无比巨大!!!
只是常数无比巨大!!!

空间复杂度

显然是O(n)的

题目

洛谷P3690 【模板】Link Cut Tree (动态树)

 #include<bits/stdc++.h>
using namespace std;
#define ImaxnF 0x7fffffff
#define ME 0x7f
#define FO(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout)
#define fui(i,a,b,c) for(int i=(a);i<=(b);i+=(c))
#define fdi(i,a,b,c) for(int i=(a);i>=(b);i-=(c))
#define fel(i,a) for(register int i=h[a];i;i=ne[i])
#define ll long long
#define MEM(a,b) memset(a,b,sizeof(a))
#define maxn (300000+10)
int n,m;
struct Node{
int fa,son[];
int val,all;//,siz;
char ifz;
void res(){fa=son[]=son[]=val=/*siz=*/;}
}tree[maxn];
int pre[maxn],inp;
template<class T>
inline T read(T &n){
n=;int t=;char ch;
for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());(ch=='-')?t=-:n=ch-'';
for(ch=getchar();isdigit(ch);ch=getchar()) n=n*+ch-'';
return (n*=t);
}
template<class T>
T write(T n){
if(n<) putchar('-'),n=-n;
if(n>=) write(n/);putchar(n%+'');
}
template<class T>
T writeln(T n){
write(n);putchar('\n');
}
char which(int x){return x==tree[tree[x].fa].son[];}
char isroot(int x){return x!=tree[tree[x].fa].son[which(x)];}
void update(int x){tree[x].all=tree[tree[x].son[]].all^tree[tree[x].son[]].all^tree[x].val;}
void pushdown(int x){
if(tree[x].ifz){
tree[x].ifz=,swap(tree[x].son[],tree[x].son[]);
tree[tree[x].son[]].ifz^=,tree[tree[x].son[]].ifz^=;
}
}void rotate(int x){
int f=tree[x].fa,ff=tree[f].fa,c=which(x);if(!isroot(f)) tree[ff].son[which(f)]=x;
tree[x].fa=ff;tree[f].son[c]=tree[x].son[c^];tree[tree[f].son[c]].fa=f;
tree[x].son[c^]=f;tree[f].fa=x;update(f);update(x);
}void splay(int x){
int f;pre[inp=]=x;for(f=x;!isroot(f);f=tree[f].fa) pre[++inp]=tree[f].fa;fdi(i,inp,,) pushdown(pre[i]);
for(;!isroot(x);rotate(x))if(!isroot(tree[x].fa))rotate((which(tree[x].fa)^which(x))?x:tree[x].fa);//update(x);
}void access(int x){for(int pr=;x;pr=x,x=tree[x].fa)splay(x),tree[x].son[]=pr,update(x);}
void makeroot(int x){access(x);splay(x);tree[x].ifz^=;}
int find(int x){for(access(x),splay(x);tree[x].son[];x=tree[x].son[]);return x;}
void cut(int x,int y){makeroot(x);access(y);splay(y);if(tree[y].son[]==x&&!tree[y].son[]&&!tree[x].son[]&&!tree[x].son[]/*tree[y].siz==2*/)tree[y].son[]=tree[x].fa=;}
void link(int x,int y){makeroot(x);tree[x].fa=y;}
void change(int x,int y){makeroot(x);splay(x);tree[x].val=y;update(x);}
int query(int x,int y){makeroot(x);access(y);splay(y);return tree[y].all;}
int main(){
read(n);read(m);
fui(i,,n,) tree[i].val=read(tree[i].all);
fui(i,,m,){
int opt,x,y;
read(opt);read(x);read(y);
switch(opt){
case :writeln(query(x,y));break;
case :if(find(x)!=find(y)) link(x,y);break;
case :if(find(x)==find(y)) cut(x,y);break;
case :change(x,y);
}
}
return ;
}

AC代码

BZOJ2049 [Sdoi2008]Cave 洞穴勘测

 #include<bits/stdc++.h>
using namespace std;
#define ImaxnF 0x7fffffff
#define ME 0x7f
#define FO(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout)
#define fui(i,a,b,c) for(int i=(a);i<=(b);i+=(c))
#define fdi(i,a,b,c) for(int i=(a);i>=(b);i-=(c))
#define fel(i,a) for(register int i=h[a];i;i=ne[i])
#define ll long long
#define MEM(a,b) memset(a,b,sizeof(a))
#define maxn (10000+10)
int n,m;
struct Node{
int fa,son[];
char ifz;
}tree[maxn];
int pre[maxn],inp;
template<class T>
inline T read(T &n){
n=;int t=;double x=;char ch;
for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());(ch=='-')?t=-:n=ch-'';
for(ch=getchar();isdigit(ch);ch=getchar()) n=n*+ch-'';
if(ch=='.') for(ch=getchar();isdigit(ch);ch=getchar()) n+=(ch-'')/x,x*=;
return (n*=t);
}
char which(int x){return x==tree[tree[x].fa].son[];}
char isroot(int x){return x!=tree[tree[x].fa].son[which(x)];}
void pushdown(int x){
if(tree[x].ifz){
tree[x].ifz=,swap(tree[x].son[],tree[x].son[]);
tree[tree[x].son[]].ifz^=,tree[tree[x].son[]].ifz^=;
}
}
void rotate(int x){
int f=tree[x].fa,ff=tree[f].fa,c=which(x);if(!isroot(f)) tree[ff].son[which(f)]=x;
tree[x].fa=ff;tree[f].son[c]=tree[x].son[c^];tree[tree[f].son[c]].fa=f;
tree[x].son[c^]=f;tree[f].fa=x;
}
void splay(int x){
int f;pre[inp=]=x;for(f=x;!isroot(f);f=tree[f].fa) pre[++inp]=tree[f].fa;fdi(i,inp,,) pushdown(pre[i]);
for(;!isroot(x);rotate(x))if(!isroot(tree[x].fa))rotate((which(tree[x].fa)^which(x))?x:tree[x].fa);
}
void access(int x){for(int pr=;x;pr=x,x=tree[x].fa)splay(x),tree[x].son[]=pr;}
void makeroot(int x){access(x);splay(x);tree[x].ifz^=;}
int find(int x){for(access(x),splay(x);tree[x].son[];x=tree[x].son[]);return x;}
void cut(int x,int y){makeroot(x);access(y);splay(y);if(tree[y].son[]==x&&!tree[y].son[]&&!tree[x].son[]&&!tree[x].son[])tree[y].son[]=tree[x].fa=;}
void link(int x,int y){makeroot(x);tree[x].fa=y;}
int main(){
read(n);read(m);
fui(i,,m,){
int x,y;char opt;
for(opt=getchar();opt!='Q'&&opt!='C'&&opt!='D';opt=getchar());read(x);read(y);
switch(opt){
case 'Q':puts((find(x)==find(y))?"Yes":"No");break;
case 'C':if(find(x)!=find(y)) link(x,y);break;
case 'D':cut(x,y);break;
}
}
return ;
}

AC代码

 

LCT摘要的更多相关文章

  1. NOIP提高组题目归类+题解摘要(2008-2017)

    因为前几天作死立了一个flag说要把NOIP近十年的题目做一做,并写一个题目归类+题解摘要出来,所以这几天就好好的(然而还是颓废了好久)写了一些这些往年的NOIP题目. 这篇博客有什么: 近十年NOI ...

  2. java根据html生成摘要

    转自:http://java.freesion.com/article/48772295755/ 开发一个系统,需要用到这个,根据html生成你指定多少位的摘要 package com.chendao ...

  3. Atitit HTTP 认证机制基本验证 (Basic Authentication) 和摘要验证 (Digest Authentication)attilax总结

    Atitit HTTP认证机制基本验证 (Basic Authentication) 和摘要验证 (Digest Authentication)attilax总结 1.1. 最广泛使用的是基本验证 ( ...

  4. 2、摘要函数——MD2/MD4/MD5数字签名

    摘要是用来防止数据被私自改动的方法,其中用到的函数叫做摘要函数.这些函数的输入可以是任意大小的信息,但是输出是大小固定的摘要.摘要有个重要的特性:如果改变了输入信息的任何内容,即使改变一位,输出也将发 ...

  5. 前端学HTTP之摘要认证

    前面的话 上一篇介绍的基本认证便捷灵活,但极不安全.用户名和密码都是以明文形式传送的,也没有采取任何措施防止对报文的篡改.安全使用基本认证的唯一方式就是将其与SSL配合使用 摘要认证与基本认证兼容,但 ...

  6. Java 消息摘要 散列 MD5 SHA

    package xxx.common.util; import java.math.BigInteger; import java.security.MessageDigest; import jav ...

  7. rpm查询命令摘要

    任务 命令 显示软件包的相关信息 rpm -q -i NAME 列出软件包中含有的所有文件 rpm -q -i NAME 列出软件包中含有的配置文件 rpm -q -c NAME 列出软件包中含有的文 ...

  8. 一堆LCT板子

    搞了一上午LCT,真是累死了-- 以前总觉得LCT高大上不好学不好打,今天打了几遍感觉还可以嘛= =反正现在的水平应付不太难的LCT题也够用了,就这样好了,接下来专心搞网络流. 话说以前一直YY不出来 ...

  9. [Java 安全]消息摘要与数字签名

    消息摘要 算法简述 定义 它是一个唯一对应一个消息或文本的固定长度的值,它由一个单向Hash加密函数对消息进行作用而产生.如果消息在途中改变了,则接收者通过对收到消息的新产生的摘要与原摘要比较,就可知 ...

随机推荐

  1. 软件工程作业 - Week 1

    构建之法读后疑问: 初步的完成构建程序设计思路之后实现过程中发现了问题或者可以优化的地方是立马就改进还是完成之后按照步骤统一进行优化. 覆盖性测试,针对一些永远用不到只是用来预防极为极端的情况下,例如 ...

  2. 第十周PSP&进度条

    PSP 一.表格: D日期     C类型 C内容 S开始时间 E结束时间 I时间间隔 T净时间(mins) 预计花费时间(mins) 11月17号 站立会议 分配任务 13:00 13:30 0 3 ...

  3. PAT 甲级 1106 Lowest Price in Supply Chain

    https://pintia.cn/problem-sets/994805342720868352/problems/994805362341822464 A supply chain is a ne ...

  4. d3 选择器

    一.隔了一段时间没看D3了,好多api又陌生了.武林太大,唯有自强不息. D3 选择器算是学习D3的第一步吧. 跟 学习JQ一样.先熟悉下api,才能够如鱼得水,手到勤来. 二. 选择器 1.选择器 ...

  5. SQLSERVER 设置自动备份数据库

    1. SQLSERVER 简单的设置 计划任务 进行 备份数据库的操作. 首先需要打开 一些设置 执行 命令如下: sp_configure ; GO RECONFIGURE; GO sp_confi ...

  6. 2013长春网赛1005 hdu 4763 Theme Section(kmp应用)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4763 题意:给出一个字符串,问能不能在该串的前中后部找到相同的子串,输出最长的字串的长度. 分析:km ...

  7. maven测试时中文乱码问题解决方法

    pom.xml增加-Dfile.encoding=UTF-8配置,如下: <plugin> <!--升级到新版本解决控制台乱码问题--> <groupId>org. ...

  8. 一本通1649【例 2】2^k 进制数

    1649:[例 2]2^k 进制数 时间限制: 1000 ms         内存限制: 524288 KB [题目描述] 原题来自:NOIP 2006 提高组 设 r 是个 2k 进制数,并满足以 ...

  9. SSM 关于service和dao的封装

    近期由于客户需求,所以我们整个小组开始开发java项目. 虽然很久以前也是系统学习过.不过干了这么多年 .net  ,有关java的早就扔了. 好了,废话不多说.我们看看SSM 关于service和d ...

  10. BZOJ2738 矩阵乘法 【整体二分 + BIT】

    题目链接 BZOJ2738 题解 将矩阵中的位置取出来按权值排序 直接整体二分 + 二维BIT即可 #include<algorithm> #include<iostream> ...