lct(linkcuttree)动态树(代码片段)

ivan-count ivan-count     2022-12-17     738

关键词:

模板参考:https://blog.csdn.net/saramanda/article/details/55253627

几个知识点:

1、LCT中用Splay维护链,这些Splay叫做“辅助树“。辅助树以它上面每个节点的深度为关键字维护,就是辅助树中每个节点左儿子的深度小于当前节点的深度,当前节点的深度小于右儿子的深度。

2、LCT相当于多棵splay被虚线连在一起,即splay森林;而最开始的时候是N个单独的点与他的父亲用虚线相连,每个点是一棵splay。

3、无论树怎样旋转,怎样变换,读入时所连接(link)的边(没有被cut的),一直都连着的

4、在每棵splay中每一个结点左子树中的节点都是他在原树中的祖先,右子树中的结点都是他在原树中的孩子。

5、splay森林实例:

原树:

技术分享图片

 

一种可能的splay森林:

技术分享图片

6、access(x)操作:

技术分享图片

 

 

—————————题目—————————

1、Cave 洞穴勘测 HYSBZ - 2049

  题意:一开始有n个洞穴,两两之间没有通道。每次将两个洞穴连接或者两个洞穴之间的通道摧毁,或者询问两个洞穴之间能否连通。

  思路:LCT模板题。连接则通过link(u,v)实现,摧毁通过cut(u,v)实现,两个洞穴能否连通则考虑u的根和v的根是否相同。

技术分享图片
  1 #include<iostream>
  2 #include<cstdio>
  3 using namespace std;
  4 const int maxn = 10000 + 10;
  5 struct LCT
  6 
  7     struct node
  8     
  9         int fa, ch[2]; //父亲(Splay对应的链向上由轻边连着哪个节点)、左右儿子
 10         int reverse;//区间反转标记
 11         bool  is_root;   //是否是所在Splay的根
 12                          //int siz;
 13                          //int val;
 14                          //int sum;
 15     Tree[maxn];
 16     int n;
 17     //int v[maxn];//每个结点的值
 18     void init()
 19     
 20         for (int i = 1; i <= n; i++)
 21         
 22             Tree[i].reverse = Tree[i].fa = Tree[i].ch[0] = Tree[i].ch[1] = 0;
 23             Tree[i].is_root = true;
 24             //Tree[i].siz = 1;
 25             //Tree[i].val = Tree[i].sum = v[i];
 26         
 27     
 28 
 29     bool getson(int x)
 30     //x是否为重儿子
 31         return x == Tree[Tree[x].fa].ch[1];
 32     
 33     bool isroot(int x)
 34     
 35         return Tree[Tree[x].fa].ch[0] != x && Tree[Tree[x].fa].ch[1] != x;
 36     
 37     void pushreverse(int x)
 38     
 39         if (!x)return;
 40         swap(Tree[x].ch[0], Tree[x].ch[1]);
 41         Tree[x].reverse ^= 1;
 42     
 43     void pushdown(int x)
 44     //下传反转标记
 45         if (Tree[x].reverse)
 46         
 47             pushreverse(Tree[x].ch[0]);
 48             pushreverse(Tree[x].ch[1]);
 49             Tree[x].reverse = 0;
 50         
 51     
 52     /*
 53     void update(int x)
 54     
 55     int l = Tree[x].ch[0], r = Tree[x].ch[1];
 56     Tree[x].siz = Tree[l].siz + Tree[r].siz + 1;
 57     Tree[x].sum = Tree[l].sum + Tree[r].sum + Tree[x].val;
 58     
 59     */
 60     void rotate(int x)
 61     //将x旋转为根
 62         if (Tree[x].is_root)return;
 63         int k = getson(x), fa = Tree[x].fa;
 64         int fafa = Tree[fa].fa;
 65         pushdown(fa); pushdown(x);    //先要下传标记
 66         Tree[fa].ch[k] = Tree[x].ch[k ^ 1];
 67         if (Tree[x].ch[k ^ 1])Tree[Tree[x].ch[k ^ 1]].fa = fa;
 68         Tree[x].ch[k ^ 1] = fa;
 69         Tree[fa].fa = x;
 70         Tree[x].fa = fafa;
 71         if (!Tree[fa].is_root)Tree[fafa].ch[fa == Tree[fafa].ch[1]] = x;
 72         else Tree[x].is_root = true, Tree[fa].is_root = false;
 73         //update(fa);update(x);    //如果维护了信息,就要更新节点
 74     
 75     void push(int x)
 76     
 77         if (!Tree[x].is_root) push(Tree[x].fa);
 78         pushdown(x);
 79     
 80     int getFa(int x)
 81     //寻找x在原树的父亲
 82         access(x);
 83         Splay(x);
 84         while (Tree[x].ch[0]) x = Tree[x].ch[0];
 85         return x;
 86     
 87     void Splay(int x)
 88     //让x成为Splay的根
 89         push(x);   //在Splay到根之前,必须先传完反转标记
 90         for (int fa; !Tree[x].is_root; rotate(x)) 
 91             if (!Tree[fa = Tree[x].fa].is_root) 
 92                 rotate((getson(x) == getson(fa)) ? fa : x);
 93             
 94         
 95     
 96     void access(int x)
 97     //访问某节点。作用是:对于访问的节点x,打通一条从树根(真实的LCT树)到x的重链;如果x往下是重链,那么把x往下的重边改成轻边。
 98         int y = 0;
 99         do 
100             Splay(x);
101             Tree[Tree[x].ch[1]].is_root = true;
102             Tree[Tree[x].ch[1] = y].is_root = false;
103             //update(x);    //如果维护了信息记得更新。
104             x = Tree[y = x].fa;
105          while (x);
106     
107     void mroot(int x)
108     //把某个节点变成树根(这里的根指的是整棵LCT的根)
109         access(x);//使x与根结点处在同一棵splay中
110         Splay(x);//x成为这棵splay的根,x只有左儿子
111         pushreverse(x);
112     
113     void link(int u, int v)
114     //连接u所在的LCT和v所在的LCT
115         mroot(u);//先让u成为其所在LCT的根
116         Tree[u].fa = v;
117     
118     void cut(int u, int v)
119     //分离出两棵LCT
120         mroot(u);   //先让u成为其所在LCT的根
121         access(v); //让u和v在同一棵Splay中
122         Splay(v);    //连接u、v,u是v的左儿子
123         pushdown(v);     //先下传标记
124         Tree[Tree[v].ch[0]].fa = Tree[v].fa;
125         Tree[Tree[v].ch[0]].is_root = true;
126         Tree[v].fa = 0;
127         Tree[v].ch[0] = 0;
128         //v的左孩子表示v上方相连的重链
129         //update(v);  //记得维护信息
130     
131     bool judge(int u, int v)
132     //判断u和v是否连通
133         while (Tree[u].fa) u = Tree[u].fa;
134         while (Tree[v].fa) v = Tree[v].fa;
135         return u == v;
136     
137 lct;
138 int main()
139 
140     int m;
141     while (~scanf("%d%d", &lct.n, &m))
142     
143         lct.init();
144         char op[50];
145         int u, v;
146         for (int i = 1; i <= m; i++)
147         
148             scanf("%s%d%d", op, &u, &v);
149             if (op[0] == Q)
150             
151                 if (lct.judge(u, v)) printf("Yes
");
152                 else printf("No
");
153             
154             else if (op[0] == C)
155             
156                 lct.link(u, v);
157             
158             else
159             
160                 lct.cut(u, v);
161             
162         
163     
164     return 0;
165 
View Code

2、Bounce 弹飞绵羊 HYSBZ - 2002

  题意:有n个弹射装置,当绵羊在第i个弹射装置时,会被弹射到第i+val[i]个弹射装置,val数组记录每个弹射装置的弹射系数。有两个操作,要么询问当绵羊站在第x个弹射装置时会经过多少次被弹飞,要么修改某个弹射装置的系数。

  思路:可以将第i个弹射装置与第i+val[i]个装置相连,新增第n+1个点作为树根,表示被弹飞。询问次数时即询问当前x到树根的距离即深度,然后-1即可(第n+1个结点不弹射);修改某个弹射装置的系数相当于修改某个结点的父亲,先将原父亲删除,再重新连接父亲。

技术分享图片
  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 using namespace std;
  5 const int maxn = 200000 + 10;
  6 int val[maxn];
  7 struct LCT
  8 
  9     struct node
 10     
 11         int fa, ch[2]; //父亲(Splay对应的链向上由轻边连着哪个节点)、左右儿子
 12         int reverse;//区间反转标记
 13         bool  is_root;   //是否是所在Splay的根
 14         int siz;
 15     Tree[maxn];
 16     int n;
 17     void init(int maxn)
 18     
 19         for (int i = 1; i <= maxn; i++)
 20         
 21             Tree[i].reverse = Tree[i].fa = Tree[i].ch[0] = Tree[i].ch[1] = 0;
 22             Tree[i].is_root = true;
 23             Tree[i].siz = 1;
 24         
 25     
 26 
 27     bool getson(int x)
 28     //x是否为重儿子
 29         return x == Tree[Tree[x].fa].ch[1];
 30     
 31     bool isroot(int x)
 32     
 33         return Tree[Tree[x].fa].ch[0] != x && Tree[Tree[x].fa].ch[1] != x;
 34     
 35     void pushreverse(int x)
 36     
 37         if (!x)return;
 38         swap(Tree[x].ch[0], Tree[x].ch[1]);
 39         Tree[x].reverse ^= 1;
 40     
 41     void pushdown(int x)
 42     //下传反转标记
 43         if (Tree[x].reverse)
 44         
 45             pushreverse(Tree[x].ch[0]);
 46             pushreverse(Tree[x].ch[1]);
 47             Tree[x].reverse = 0;
 48         
 49     
 50 
 51     void update(int x)
 52     
 53         int l = Tree[x].ch[0], r = Tree[x].ch[1];
 54         Tree[x].siz = 1;
 55         if (l) Tree[x].siz += Tree[l].siz;
 56         if (r) Tree[x].siz += Tree[r].siz;
 57     
 58 
 59     void rotate(int x)
 60     //将x旋转为根
 61         if (Tree[x].is_root)return;
 62         int k = getson(x), fa = Tree[x].fa;
 63         int fafa = Tree[fa].fa;
 64         pushdown(fa); pushdown(x);    //先要下传标记
 65         Tree[fa].ch[k] = Tree[x].ch[k ^ 1];
 66         if (Tree[x].ch[k ^ 1])Tree[Tree[x].ch[k ^ 1]].fa = fa;
 67         Tree[x].ch[k ^ 1] = fa;
 68         Tree[fa].fa = x;
 69         Tree[x].fa = fafa;
 70         if (!Tree[fa].is_root)Tree[fafa].ch[fa == Tree[fafa].ch[1]] = x;
 71         else Tree[x].is_root = true, Tree[fa].is_root = false;
 72         update(fa);update(x);    //如果维护了信息,就要更新节点
 73     
 74     void push(int x)
 75     
 76         if (!Tree[x].is_root) push(Tree[x].fa);
 77         pushdown(x);
 78     
 79     int getFa(int x)
 80     //寻找x在原树的父亲
 81         access(x);
 82         Splay(x);
 83         while (Tree[x].ch[0]) x = Tree[x].ch[0];
 84         return x;
 85     
 86     void Splay(int x)
 87     //让x成为Splay的根
 88         push(x);   //在Splay到根之前,必须先传完反转标记
 89         for (int fa; !Tree[x].is_root; rotate(x)) 
 90             if (!Tree[fa = Tree[x].fa].is_root) 
 91                 rotate((getson(x) == getson(fa)) ? fa : x);
 92             
 93         
 94     
 95     void access(int x)
 96     //访问某节点。作用是:对于访问的节点x,打通一条从树根(真实的LCT树)到x的重链;如果x往下是重链,那么把x往下的重边改成轻边。
 97         int y = 0;
 98         do 
 99             Splay(x);
100             Tree[Tree[x].ch[1]].is_root = true;
101             Tree[Tree[x].ch[1] = y].is_root = false;
102             update(x);    //如果维护了信息记得更新。
103             x = Tree[y = x].fa;
104          while (x);
105     
106     void mroot(int x)
107     //把某个节点变成树根(这里的根指的是整棵LCT的根)
108         access(x);//使x与根结点处在同一棵splay中
109         Splay(x);//x成为这棵splay的根,x只有左儿子
110         pushreverse(x);
111     
112     void link(int u, int v)
113     //连接u所在的LCT和v所在的LCT
114         mroot(u);//先让u成为其所在LCT的根
115         Tree[u].fa = v;
116         Tree[u].is_root = true;
117     
118     void cut(int u, int v)
119     //分离出两棵LCT
120         mroot(u);   //先让u成为其所在LCT的根
121         access(v); //让u和v在同一棵Splay中
122         Splay(v);    //连接u、v,u是v的左儿子
123         pushdown(v);     //先下传标记
124         Tree[Tree[v].ch[0]].fa = Tree[v].fa;
125         Tree[Tree[v].ch[0]].is_root = true;
126         Tree[v].fa = 0;
127         Tree[v].ch[0] = 0;
128         //v的左孩子表示v上方相连的重链
129         update(v);  //记得维护信息
130     
131     bool judge(int u, int v)
132     //判断u和v是否连通
133         while (Tree[u].fa) u = Tree[u].fa;
134         while (Tree[v].fa) v = Tree[v].fa;
135         return u == v;
136     
137     int Query_deep(int x)
138     //询问x到LCT根的距离(深度)
139         access(x);
140         Splay(x);
141         return Tree[x].siz;
142     
143 lct;
144 int main()
145 
146     int m;
147     while (~scanf("%d", &lct.n))
148     
149         lct.init(lct.n+1);////让n+1表示被弹飞
150         for (int i = 1; i <= lct.n; i++)
151         
152             scanf("%d", &val[i]);
153             int id = min(i + val[i], lct.n + 1);
154             lct.link(i, id);
155         
156         lct.mroot(lct.n + 1);
157         scanf("%d", &m);
158         for (int i = 1; i <= m; i++)
159         
160             int op, x;
161             scanf("%d%d", &op, &x);
162             x++;
163             if (op == 1) printf("%d
", lct.Query_deep(x)-1);
164             else
165             
166                 int v;
167                 scanf("%d", &v);
168                 int oid = min(x + val[x], lct.n + 1);
169                 int nid = min(x + v, lct.n + 1);
170                 lct.cut(x, oid);
171                 lct.link(x, nid);
172                 lct.mroot(lct.n + 1);
173                 val[x] = v;
174             
175         
176     
177     return 0;
178 
View Code

 

luogup3690模板linkcuttree(动态树)lct模板

P3690【模板】LinkCutTree(动态树)题目背景动态树题目描述给定n个点以及每个点的权值,要你处理接下来的m个操作。操作有4种。操作从0到3编号。点从1到n编号。0:后接两个整数(x,y),代表询问从x到y的路径上的点的权值的xor和... 查看详情

luogup3690模板linkcuttree(动态树)[lct]

题目背景动态树题目描述给定N个点以及每个点的权值,要你处理接下来的M个操作。操作有4种。操作从0到3编号。点从1到N编号。0:后接两个整数(x,y),代表询问从x到y的路径上的点的权值的xor和。保证... 查看详情

数据结构专题-学习笔记:linkcuttree动态树

1.前言LinkCutTree动态树,简称LCT,是一种维护动态森林的数据结构。前置知识:Splay。2.LCT例题:P3690【模板】动态树(LinkCutTree)2.1实链剖分实链剖分也是一种对树的剖分方式,类似于重链剖分和长链剖分,其将一棵树上的边,随... 查看详情

p3690模板linkcuttree(动态树)(代码片段)

哇,做梦也没想到我居然能写LCT题意:给定n个点以及每个点的权值,要你处理接下来的m个操作。操作有4种。操作从0到3编号。点从1到n编号。0:后接两个整数(x,y),代表询问从x到y的路径上的点的权值的xor和。保证x到y是联通... 查看详情

洛谷p3690模板linkcuttree(lct)(代码片段)

题目背景动态树题目描述给定n个点以及每个点的权值,要你处理接下来的m个操作。操作有4种。操作从0到3编号。点从1到n编号。0:后接两个整数(x,y),代表询问从x到y的路径上的点的权值的xor和。保证x到y是联通的。1:后接两... 查看详情

linkcuttree学习笔记

从这里开始动态树问题和LinkCutTree一些定义access操作换根操作link和cut操作时间复杂度证明LinkCutTree维护链上信息LinkCutTree维护子树信息小结动态树问题和LinkCutTree  动态树问题是一类要求维护一个有根树森林,支持对树的分割,... 查看详情

linkcuttree求最小生成树(代码片段)

对边建点,原图中的边转化为点的点-边的点-点的点于是用LCT维护连通关系,并支持查询最大值位置即可#include<bits/stdc++.h>usingnamespacestd;constintN=300005;intn,m,val[N],t1,t2,t3;namespacelct inttop,q[N],ch[N][2],fa[N],rev[N]; intmx[N]; inlin 查看详情

p3690linkcuttree(动态树)

干脆整个LCT模板吧。缺个链上修改和子树操作,链上修改的话join(u,v)然后把vsplay到树根再打个标记就好。至于子树操作...以后有空的话再学(咕咕咕警告)1#include<bits/stdc++.h>2usingnamespacestd;3typedeflonglongll;4constintN=1e5+10;5intn,m,a[N],X... 查看详情

刷题洛谷p3690模板linkcuttree(动态树)(代码片段)

题目背景动态树题目描述给定n个点以及每个点的权值,要你处理接下来的m个操作。操作有4种。操作从0到3编号。点从1到n编号。0:后接两个整数(x,y),代表询问从x到y的路径上的点的权值的xor和。保证x到y是联通的。1:后接两... 查看详情

p3690模板linkcuttree(动态树)(代码片段)

P3690【模板】LinkCutTree(动态树)注意:不要把$fa[x]$和$nrt(x)$混在一起!#include<cstdio>voidswap(int&a,int&b)a^=b^=a^=b;#defineN300005intn,m,s[N],v[N],ch[2][N],fa[N],rev[N];#definelcch[0][x]#definercch[1][x]boolnrt(intx)returnch[0][fa[x]]==x||ch[1][fa[x]]==x;voidu... 查看详情

lct(代码片段)

(LinkCutTree)(动态树)用实链剖分来实现,维护的对象为一个森林,将原树剖分为若干个辅助树,辅助树用(Splay)来维护辅助树内部用实边连接,辅助树之间用虚边连接,虚边总是由一棵(Splay)指向另一棵(Splay)的根,即为其中序遍... 查看详情

luogu3690模板linkcuttree(动态树)

题目luogu3690硫硼作者想提醒大家,WA了TLE了RE了的,也许只是主函数写错了代码#include<iostream>#include<cstdio>#include<cmath>#include<algorithm>#include<cstdlib>#include<cstring>usingnamespacestd 查看详情

link-cut-tree动态树算法(代码片段)

Link-Cut-Tree动态树算法总结动态树是一类要求维护森林连通性的算法总称,其中最常用的就是lct(Link-Cut-Tree).lct支持一下操作链上求和 链上求最值链上修改(前三项均可用树链剖分+线段树实现)断开树上的一条边连... 查看详情

动态树学习(代码片段)

 学习动态树,我们首先需要了解到什么是Splay,推荐这一篇大聚聚yyb的博客。 我们在LCT中列写的Splay会以yyb的splay为基础作出改变,也是方便大家的后继学习,这样的排版。 LCT主要解决什么问题呢?维护一个数据结构... 查看详情

p3690模板linkcuttree(动态树)(代码片段)

LCTLCT即Link-Cut-Tree维护一个森林,支持很多操作,比如:维护链上信息(min,max,sum,xor。。。。。。)换根动态维护联通性维护子树信息概念虚边:连接儿子与父亲,儿子记录父亲,父亲不记录儿子(父不认子)实边:父子互... 查看详情

[模板]动态树/lct(代码片段)

简介LCT是一种数据结构,可以维护树的动态加边,删边,维护链上信息(满足结合律),单次操作时间复杂度(O(logn)).(不会证)思想类似树链剖分,因为splay可以换根,用splay维护重链,splay的中序遍历即为链按深度从小到大遍历的结果.注意区... 查看详情

luogu3690模板linkcuttree(动态树)

参考there和there#include<iostream>#include<cstdio>usingnamespacestd;intn,m,val[300005],ch[300005][2],sum[300005],fa[300005],uu,vv,opt;intrev[300005];voidpushDown(intx)if(rev[x])swap(ch[x][0 查看详情

luogup3690模板linkcuttree(动态树)

#include<iostream>#include<cstring>#include<algorithm>#include<cmath>#include<cstdlib>#include<climits>#include<stack>#include<vector>#include<queue& 查看详情