LCT摘要
介绍、用途
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代码
- #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摘要的更多相关文章
- NOIP提高组题目归类+题解摘要(2008-2017)
因为前几天作死立了一个flag说要把NOIP近十年的题目做一做,并写一个题目归类+题解摘要出来,所以这几天就好好的(然而还是颓废了好久)写了一些这些往年的NOIP题目. 这篇博客有什么: 近十年NOI ...
- java根据html生成摘要
转自:http://java.freesion.com/article/48772295755/ 开发一个系统,需要用到这个,根据html生成你指定多少位的摘要 package com.chendao ...
- Atitit HTTP 认证机制基本验证 (Basic Authentication) 和摘要验证 (Digest Authentication)attilax总结
Atitit HTTP认证机制基本验证 (Basic Authentication) 和摘要验证 (Digest Authentication)attilax总结 1.1. 最广泛使用的是基本验证 ( ...
- 2、摘要函数——MD2/MD4/MD5数字签名
摘要是用来防止数据被私自改动的方法,其中用到的函数叫做摘要函数.这些函数的输入可以是任意大小的信息,但是输出是大小固定的摘要.摘要有个重要的特性:如果改变了输入信息的任何内容,即使改变一位,输出也将发 ...
- 前端学HTTP之摘要认证
前面的话 上一篇介绍的基本认证便捷灵活,但极不安全.用户名和密码都是以明文形式传送的,也没有采取任何措施防止对报文的篡改.安全使用基本认证的唯一方式就是将其与SSL配合使用 摘要认证与基本认证兼容,但 ...
- Java 消息摘要 散列 MD5 SHA
package xxx.common.util; import java.math.BigInteger; import java.security.MessageDigest; import jav ...
- rpm查询命令摘要
任务 命令 显示软件包的相关信息 rpm -q -i NAME 列出软件包中含有的所有文件 rpm -q -i NAME 列出软件包中含有的配置文件 rpm -q -c NAME 列出软件包中含有的文 ...
- 一堆LCT板子
搞了一上午LCT,真是累死了-- 以前总觉得LCT高大上不好学不好打,今天打了几遍感觉还可以嘛= =反正现在的水平应付不太难的LCT题也够用了,就这样好了,接下来专心搞网络流. 话说以前一直YY不出来 ...
- [Java 安全]消息摘要与数字签名
消息摘要 算法简述 定义 它是一个唯一对应一个消息或文本的固定长度的值,它由一个单向Hash加密函数对消息进行作用而产生.如果消息在途中改变了,则接收者通过对收到消息的新产生的摘要与原摘要比较,就可知 ...
随机推荐
- 开源中文分词框架分词效果对比smartcn与IKanalyzer
一.引言: 中文分词一直是自然语言处理的一个痛处,早在08年的时候,就曾经有项目涉及到相关的应用(Lunce构建全文搜索引擎),那时的痛,没想到5年后的今天依然存在,切分效果.扩展支持.业务应用等方面 ...
- <转>HTML、CSS、font-family:中文字体的英文名称
宋体 SimSun 黑体 SimHei 微软雅黑 Microsoft YaHei 微软正黑体 Microsoft JhengHei 新宋体 NSimSun 新细明体 PMingLiU 细明体 Ming ...
- Rendertron:谷歌 Chrome 新的 headless 模式又贡献了一个新的技巧
摘自:https://zhuanlan.zhihu.com/p/31670033 Rendertron:JavaScript Web 富应用的一个老问题是如何使这些页面的动态渲染部分可供搜索引擎检索. ...
- redis后台启动配置
在cmd窗口启动redis,窗口关闭后再次操作会报错. 将redis安装为服务,可使其在后台启动,无须担心误操作关闭服务窗口. 配置如下: 进入redis目录,输入如下命令执行即可: redis-se ...
- BZOJ2756 SCOI2012奇怪的游戏(二分答案+最大流)
由数据范围容易想到网络流.由于操作只是对于棋盘上相邻两格,容易想到给其黑白染色. 假设已经知道最后要变成什么数.那么给黑白点之间连边,其流量则表示同时增加的次数,再用源汇给其限流为需要增加的数即可. ...
- BZOJ2741 FOTILE模拟赛L(分块+可持久化trie)
显然做个前缀和之后变成询问区间内两个数异或最大值. 一种暴力做法是建好可持久化trie后直接枚举其中一个数查询,复杂度O(nmlogv). 观察到数据范围很微妙.考虑瞎分块. 设f[i][j]为第i个 ...
- c读入实型
读入: 如果读入的数为整型,然后转为实型,则%lf 否则%f也可以 读出: %f,这样在codeblocks才能看到正确的结果
- MySQL的replace方法
mysql中replace函数直接替换mysql数据库中某字段中的特定字符串,不再需要自己写函数去替换,用起来非常的方便,mysql 替换函数replace()Update `table_name` ...
- Scala进阶之路-高级数据类型之集合的使用
Scala进阶之路-高级数据类型之集合的使用 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. Scala 的集合有三大类:序列 Seq.集 Set.映射 Map,所有的集合都扩展自 ...
- logback常见配置
依赖jar包 <!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-core --> <dependency& ...