前言:这个算法似乎机房全都会,就我不会了TAT...强行搞了很久,勉强照着别人代码抄了一遍qwq

这个本人看论文实在看不懂,太菜了啊!!! 只好直接看如何实现...可是实现也看不太懂...

但直到我看到一篇大佬博客!!! point here 是真的讲的好,一点都不敷衍.真没收钱

本篇比较干货qwq(没图啊!!!) 一定要耐住性子学算法!!!

概念辨析

  • 但在之前还是要辨析几个概念:

辅助树

本人理解就是对于原树抽象出一个splay 或者说很多棵splay (啥,你告诉我原树是多叉树,splay只是一个二叉树)

其实就是将一个splay划分成很多部分(每一部分维护原图中的一条链) 然后对于这些部分连轻边.

(建议先认真看看那些基础概念) 然后对于一条链来说,深度两两不同,我们就可以用这个作为splay的键值来排序

然后这就可以维护出原树了... 为什么呢? 因为 虽然父亲只能有两个儿子,但是有很多儿子来认他啊qwq

(就像造树的时候只要给每个点一个fa就行了) 然后每条链又在splay中可以确定他们的先后顺序(也就是深度大小)

轻边和重边

这个不像树剖,这个就是随便给的(根据操作access来改变) 然后重边存在于splay中的子认父也认 的边, 也就是维护链时候的边. 然后轻边,就是只连到父亲时子认父不认 的边,就是一颗splay连到重链顶端父亲的那条边,而且一个点只能有一个重边!

操作介绍

然后就直接介绍操作吧(你还是没听懂?那看看别人博客的概念介绍吧qwq)

access

就是拉链,就是把一个点到根节点路径全都变为重边,且它是这条路径的一个端点.

如何实现呢qwq... 就是每次把当前这个节点splay到根,然后这个splay的父亲认了他这个儿子就行了,

接下来上传信息... 不断操作,直到到根 (注意要把他变为树的一个端点,所以要一开始要将它的儿子认为空的)

access=splay+child+push_up

make_root

使得一个点变为原树的根,然后这就可以便于维护路径信息了.

首先先把这个点access到根,之后将这个点splay到当前splay的根,然后再下放一个rev的标记就行了.

这是因为你使当前重链的深度完全翻转了(注意,我们splay是按照深度排序的!!!)

x就变为深度最小的点(即根节点)

make_root=access+splay+rev

find_root

找到原树的根,这个就易于判断连通性了(类似于并查集)

又是access到根,然后也是splay到根. (这样似乎很好维护一些东西,而且更方便去想了)

然后直接一直向左边走,最下面那个就是根节点了(深度最小, 时间复杂度因为有双旋所以可以保证)

而且在走左子树的时候要push_down!! (大佬博客上讲的,我还没被坑过qwq)

find_root=access+splay+go_left_child

split

将原图中的一条路径变为一条以它们为端点的重链.

假设我们split(x,y). 首先先把make_root(x)便于操作. 然后access(y),拉一条路径出来.

为了查找信息我们splay(y)splay的根上去,直接访问y的信息(中间splaypush_up)

split(x,y)=make_root(x)+access(y)+splay(y)

link

将原图中的两个点连一条边

我们把link(x,y)定义为把x的父亲认做y. (其实你互换也是可以的)

如果操作合法,我们只要make_root(x)然后x的父亲认做y就行了.

就是让x变为他所在树的根就行了qwq.. 不合法的话,就判断联通性就行了.

link(x,y)=make_root(x)+father[x]=y

cut

将原图中的一条边断掉

我们同样把cut(x,y)定义为把原图中x与其父亲y的边断掉. (同上互换也是可以的)

这个合法的话,直接把他们中的那条链split出来,直接断掉就行了...

不合法的话,同样尝试split出来,如果x的左儿子不是y就不行.

这样意义就是y在原树中不是x的父亲,不能cut掉.

cut(x,y)=split(x,y)+left_child[y]=fa[x]=0

至此link_cut_tree所有基础知识已经讲完qwq...然后高端操作只能自己做题了...

细节问题

这个lct细节是真的多,一不小心就调不出来了啊!!!

  1. rotate和普通的不同,要判断是不是当前splay的根!!!!

而且连边的时候一定要注意顺序啊!!!

就是判断is_root(u)之前不能连u-v的那条边!!!!

  1. splay那里也是 判断is_root的时候要注意对象啊,是fa[u]!!!

  2. make_root那里不能先交换一遍!!!直接打标记在那里就行了!!!

(有些人写法是先交换,再打左右儿子标记,我代码不同啊!!!)

  1. splayt要从o开始,然后判断!is_root(t)而不能从fa[o]开始,这是因为fa[o]可能为\(0\)啊!!!

  2. rotate中是push_up而不是push_down,前面splaypush_down完了啊!!!

代码

(没有注释....凑合对对,这道题是luogu模板)

#include <bits/stdc++.h>
#define debug cerr << "Pass" << endl
#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
using namespace std; inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;} inline int read() {
int x = 0, fh = 1; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1;
for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
return x * fh;
} void File() {
#ifdef zjp_shadow
freopen ("P3690.in", "r", stdin);
freopen ("P3690.out", "w", stdout);
#endif
} const int maxn = 1e6 + 1e3; int val[maxn];
#define ls(u) ch[u][0]
#define rs(u) ch[u][1]
struct Link_Cut_Tree {
int fa[maxn], ch[maxn][2], rev[maxn], xsum[maxn]; inline bool is_root(int o) { return o != ls(fa[o]) && o != rs(fa[o]); } inline bool get(int o) { return o == rs(fa[o]); } inline void push_up(int o) { xsum[o] = xsum[ls(o)] ^ xsum[rs(o)] ^ val[o]; } inline void push_down(int o) {
if (rev[o]) { swap(ls(o), rs(o)); rev[ls(o)] ^= 1; rev[rs(o)] ^= 1; rev[o] = 0; }
} inline void rotate(int v) {
int u = fa[v], t = fa[u], d = get(v);
fa[ch[u][d] = ch[v][d ^ 1]] = u;
fa[v] = t; if (!is_root(u)) ch[t][rs(t) == u] = v;
fa[ch[v][d ^ 1] = u] = v;
push_up(u); push_up(v);
} int sta[maxn], top;
inline void splay(int u) {
sta[top = 1] = u;
for (register int t = u; !is_root(t); t = fa[t]) sta[++ top] = fa[t];
while (top) push_down(sta[top --]);
for (; !is_root(u); rotate(u)) if (!is_root(fa[u])) rotate(get(u) ^ get(fa[u]) ? u : fa[u]);
} inline void access(int o) { for (int t = 0; o; o = fa[t = o]) splay(o), rs(o) = t, push_up(o); } inline void make_root(int o) { access(o); splay(o); rev[o] ^= 1; } inline int find_root(int o) { access(o); splay(o); while (ls(o)) o = ls(o), push_down(o); splay(o); return o; } inline void split(int v, int u) { make_root(v); access(u); splay(u); } inline bool link(int v, int u) { make_root(v); if (find_root(u) == v) return false; fa[v] = u; return true; } inline void cut(int v, int u) { split(v, u); if (ls(u) == v) ls(u) = fa[v] = 0; }
} lct; int n, m; int main () {
File();
n = read(); m = read();
For (i, 1, n) val[i] = lct.xsum[i] = read();
For (i, 1, m) {
int opt = read(), x = read(), y = read();
if (!opt) { lct.split(x, y); printf ("%d\n", lct.xsum[y]); }
else if (opt == 1) lct.link(x, y);
else if (opt == 2) lct.cut(x, y);
else { lct.access(x); lct.splay(x); val[x] = y; lct.push_up(x); }
}
return 0;
}

link-cut-tree 简单介绍的更多相关文章

  1. LCT总结——概念篇+洛谷P3690[模板]Link Cut Tree(动态树)(LCT,Splay)

    为了优化体验(其实是强迫症),蒟蒻把总结拆成了两篇,方便不同学习阶段的Dalao们切换. LCT总结--应用篇戳这里 概念.性质简述 首先介绍一下链剖分的概念(感谢laofu的讲课) 链剖分,是指一类 ...

  2. link cut tree 入门

    鉴于最近写bzoj还有51nod都出现写不动的现象,决定学习一波厉害的算法/数据结构. link cut tree:研究popoqqq那个神ppt. bzoj1036:维护access操作就可以了. ...

  3. Link/cut Tree

    Link/cut Tree 一棵link/cut tree是一种用以表示一个森林,一个有根树集合的数据结构.它提供以下操作: 向森林中加入一棵只有一个点的树. 将一个点及其子树从其所在的树上断开. 将 ...

  4. Link Cut Tree学习笔记

    从这里开始 动态树问题和Link Cut Tree 一些定义 access操作 换根操作 link和cut操作 时间复杂度证明 Link Cut Tree维护链上信息 Link Cut Tree维护子 ...

  5. B - Link/Cut Tree

    Problem description Programmer Rostislav got seriously interested in the Link/Cut Tree data structur ...

  6. [BJOI2014]大融合(Link Cut Tree)

    [BJOI2014]大融合(Link Cut Tree) 题面 给出一棵树,动态加边,动态查询通过每条边的简单路径数量. 分析 通过每条边的简单路径数量显然等于边两侧节点x,y子树大小的乘积. 我们知 ...

  7. 学习笔记:Link Cut Tree

    模板题 原理 类似树链剖分对重儿子/长儿子剖分,Link Cut Tree 也做的是类似的链剖分. 每个节点选出 \(0 / 1\) 个儿子作为实儿子,剩下是虚儿子.对应的边是实边/虚边,虚实时可以进 ...

  8. Codeforces Round #339 (Div. 2) A. Link/Cut Tree 水题

    A. Link/Cut Tree 题目连接: http://www.codeforces.com/contest/614/problem/A Description Programmer Rostis ...

  9. 洛谷P3690 Link Cut Tree (模板)

    Link Cut Tree 刚开始写了个指针版..调了一天然后放弃了.. 最后还是学了黄学长的板子!! #include <bits/stdc++.h> #define INF 0x3f3 ...

  10. bzoj2049 [Sdoi2008]Cave 洞穴勘测 link cut tree入门

    link cut tree入门题 首先说明本人只会写自底向上的数组版(都说了不写指针.不写自顶向下QAQ……) 突然发现link cut tree不难写... 说一下各个函数作用: bool isro ...

随机推荐

  1. linux下LAMP环境的搭配

    之前电脑上换了ubuntu16.04,本地需要重新配置,但是忘得一干二净,所以重新配置了一下,并再此记录一下. 安装apache: sudo apt-get install apache2 重启apa ...

  2. 3、flask之基于DBUtils实现数据库连接池、本地线程、上下文

    本篇导航: 数据库连接池 本地线程 上下文管理 面向对象部分知识点解析 1.子类继承父类__init__的三种方式 class Dog(Animal): #子类 派生类 def __init__(se ...

  3. ireport报表学习

    常用组件介绍: 制作一个报表一般四个组件比较常用,下面分别介绍 Rectangle:用于画表格的样式,整个表格的样式使用次组件做出来的,本控件表现为一个黑色矩形框,多个黑色矩形框排在一起可以组合出来任 ...

  4. [翻译]编写高性能 .NET 代码 第一章:工具介绍 -- Performance Counters(性能计数器)

    <<返回目录 Performance Counters(性能计数器) 性能计数器是监视应用程序和系统性能的最简单的方法之一.它有几十个类别数百个计数器在,包括一些.net特有的计数器.要访 ...

  5. django-表单

    一.从Request对象中获取信息 1.URL相关的信息 属性/方法 说明 举例 request.path 除域名以外的请求路径,以正斜杠开头 "/hello/" request. ...

  6. C编程之 一个容易忽视但是十分严重的小错误

    while(...) { ...if(a=b) continue; } 调试时就一直执行continue.一直找不到原因,后面才发现是少一个"=": 还有一次就是也是在if中,if ...

  7. 《设计模式之禅》--备忘录扩展:clone方式的备忘录

    接上篇<设计模式之禅>--策略扩展:策略枚举 需求:使用clone方式实现备忘录模式 发起人角色 public class Originator implements Cloneable ...

  8. hihoCoder 1051 : 补提交卡 枚举

    思路:预处理cnt(i)表示前i个数中有多少天需要补提交卡,枚举各个连续区间,区间[j, i]中需要补提交卡的天数是cnt(i) - cnt(j-1),判断m是否大于等于cnt(i) - cnt(j- ...

  9. hdu 2044 递推

    到达第n个格子的方案数等于第n-1个格子的方案数加上第n-2个格子的方案数. d[i]=d[i-1]+d[i-2]; AC代码: #include<cstdio> const int ma ...

  10. ANTD mobile源码分析 -- popover

    最近的开发中要用到很多的各式各样的组件.但是发现ant design mobile(后面简称ANTDM)里很多的资源.于是就分析一下,学习学习. ANTDM直接使用了typescript,没有用ES2 ...