首页 > 其他分享 >LCT学习笔记

LCT学习笔记

时间:2023-03-15 19:57:05浏览次数:48  
标签:LCT return int void 笔记 学习 pa inline 节点

板子

从问题看起:P3690

本题需要在一个动态图上实时查询各条链的状态(异或和)。因为两点联通时不连边,所以该图始终具有以下性质:

任意联通两点间有唯一路径。

因此该图是一个森林。

LCT 存储森林中的每条边,同时可以查询森林中任意一条链。当然也可以加边或删边

思路:

对于每一棵子树,参考树链剖分,我们可以将树分为若干条链。那么每条链之间都可能有边相连。但边是动态的,这就导致链的结构不固定,无法用线段树维护。那么可以用什么来维护这么多结构不固定的链呢?

Tarjan 选择了结构同样不固定的 Splay。与树链剖分时相同,我们所维护的链中节点深度(指定某个节点为根的意义下)都是连续的。既然这样,节点深度就可以成为 Splay 的中序遍历。

也就是说我们为每一条链用一个 Splay 维护,其中序遍历即为根据从小到大遍历链上节点。

于是就有了实边与虚边。

现在主要来看在那几条定义的基础上,如何初步达到本题目标:访问任意一条链

所有的虚边都是子能认父,因此一棵子树的根节点是该树内所有节点的祖先节点。又因为该树的任何一个非根节点都知道它的父节点,所以每一个节点都能通过不断访问父节点来跳到根部

我们就可以在这样的过程中建一条链。这就是 access 函数:建立一个节点到它的根节点的链。

inline void access(int p){
    int r = p;
    for (int c = 0; p; c = p, p = a[p].pa)
        splay(p), a[p].rs = c, pushup(p);
    splay(r);
    return;
}

观察代码,发现在此过程中我们通过覆盖右 Splay 中的右子节点去除了该链上所有其他节点。这样不会影响图的结构,因为这仍然是一条虚边:被覆盖的子节点的父节点未变化。

目前为止,我们已经可以建立一条从任意节点到树根的链了。但如果我们想要的两个端点都不是树根怎么办?

把其中一个变成根不就行了:

```cpp
inline void makeroot(int p){
access(p);
revit(p);
return;
}
```

$revit$ 函数为区间反转,作用是将整个 Splay 的中序遍历倒转。当我们建出由根到需求节点的链时,由于链中只有路径上的点,$access$ 的节点必然在链上**深度最大**。此时反转整个 Splay,自然将该点变为了深度最小的节点——根节点。

现在就可以访问任意一条链了:

```cpp
inline void split(int x, int y){
makeroot(x);
access(y);
return;
}
```

至此,访问链的操作已经完成,还需要支持加边和删边。

我们需要一个判断两点是否联通的方法。很简单,只要看它们所在的树的根节点是否相同就行了。

如何找到当前节点的根?在 $access$ 后,我们得到了一条从根到当前节点的链。这条链中序遍历的第一个节点显然就是树根。但是,记得处理懒标记。

```cpp
inline int findroot(int p){
access(p);
while (p && a[p].ls) pushdown(p), p = a[p].ls;
splay(p);
return p;
}
```

根据边的定义,$link$ 和 $cut$ 如下:

```cpp
inline void link(int x, int y){
makeroot(x);
if (findroot(y) == x) return;
a[x].pa = y;
return;
}
inline void cut(int x, int y){
makeroot(x);
if (findroot(y) != x || a[y].pa != x || a[y].ls) return;
a[x].rs = a[y].pa = 0;
pushup(x);
return;
}
```


总代码:

```cpp
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define isdigit(x) ('0' <= x && x <= '9')
using namespace std;

const int MAXN = 100010;

template<typename types>
inline void read(types &x){
x = 0; int f = 1; char c = getchar();
while (!isdigit(c)){ if (c == '-') f = -1; c = getchar(); }
while (isdigit(c)) x = x * 10 + c - '0', c = getchar();
x *= f; return;
}
template<typename types>
void write(types x){
if (x < 0) putchar('-'), x = - x;
types k = x / 10;
if (k) write(k);
putchar(x - 10 * k + '0'); return;
}

int n, m;

#define ls ch[0]
#define rs ch[1]
struct Node{
int ch[2], pa;
int val, sum, rev;
} a[MAXN];
stack<int> s;
inline void pushup(int p){
a[p].sum = a[a[p].ls].sum ^ a[a[p].rs].sum ^ a[p].val;
return;
}
inline bool check(int p){
return a[a[p].pa].rs == p;
}
inline void revit(int p){
a[p].rev ^= true;
swap(a[p].ls, a[p].rs);
return;
}
inline void pushdown(int p){
if (a[p].rev){
revit(a[p].ls), revit(a[p].rs);
a[p].rev = false;
}
return;
}
inline bool isroot(int p){
return a[a[p].pa].ls != p && a[a[p].pa].rs != p;
}
inline void rotate(int x){
int y = a[x].pa, z = a[y].pa, dir = check(x), w = a[x].ch[!dir];
a[x].pa = z; if (!isroot(y)) a[z].ch[check(y)] = x;
a[y].pa = x; a[x].ch[!dir] = y;
a[w].pa = y; a[y].ch[dir] = w;
pushup(y), pushup(x);
return;
}
inline void splay(int p){
if (!p) return;
int r = p;
s.push(r);
while (!isroot(r)) s.push(r = a[r].pa);
while (!s.empty()) pushdown(s.top()), s.pop();
for (int pa = a[p].pa; pa = a[p].pa, !isroot(p); rotate(p))
if (!isroot(pa)) rotate(check(p) == check(pa) ? pa : p);
return;
}
inline void access(int p){
int r = p;
for (int c = 0; p; c = p, p = a[p].pa)
splay(p), a[p].rs = c, pushup(p);
splay(r);
return;
}
inline void makeroot(int p){
access(p);
revit(p);
return;
}
inline int findroot(int p){
access(p);
while (p && a[p].ls) pushdown(p), p = a[p].ls;
splay(p);
return p;
}
inline void split(int x, int y){
makeroot(x);
access(y);
return;
}
inline void link(int x, int y){
makeroot(x);
if (findroot(y) == x) return;
a[x].pa = y;
return;
}
inline void cut(int x, int y){
makeroot(x);
if (findroot(y) != x || a[y].pa != x || a[y].ls) return;
a[x].rs = a[y].pa = 0;
pushup(x);
return;
}

int main(){
read(n), read(m);
for (int i = 1; i <= n; ++i) read(a[i].val);
while (m--){
int op, x, y; read(op), read(x), read(y);
if (op == 0){
split(x, y);
write(a[y].sum), puts("");
}
else if (op == 1) link(x, y);
else if (op == 2) cut(x, y);
else{
makeroot(x);
a[x].val = y;
pushup(x);
}
}
return 0;
}
```

 

标签:LCT,return,int,void,笔记,学习,pa,inline,节点
From: https://www.cnblogs.com/HHH6666666666/p/17219741.html

相关文章

  • 软件学习记录:(10) 雷赛运动控制卡
    软件学习记录:(10)雷赛运动控制卡(一)定长运动控制需求分析:设置XYZ某一个轴运动定长位移设置轴归零。功能设计:定长位移流程:1.检测板卡是否上线。2.设置轴运动......
  • C++学习记录
    C++recordnotebook基础导论C++特性具有c访问硬件的能力和面向对象程序的属性,以及更具有泛型编程的功能(使用模板进行编程)。OOP(面向对象编程)其中的方法有:自顶向下和......
  • 爬虫学习02之模拟登录以及代理
    一、调试模式介绍调试模式,即进入网页页面半代码模式,查看网页与代码一一对应关系。鼠标右键,再出现选项中找到检查进入调试模式,或者按键盘上的F12键进入调试模式。功能介......
  • salesforce零基础学习(一百二十七)Custom Metadata Type 篇二
    本篇参考: salesforce零基础学习(一百一十一)custommetadatatype数据获取方式更新https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_metho......
  • python数据库学习笔记
    1.数据库管理软件的本质?本质就是C/S架构的套接字程序服务器套接字操作系统:linux计算机(本地文件)2.为什么要用数据库管理软件?可以自己写,要结合套接字程序,解决并发......
  • QT5笔记:18 QPainter基本绘图 完结撒花,感谢陪伴!!!
    代码#include"widget.h"#include"ui_widget.h"#include<QPainter>Widget::Widget(QWidget*parent):QWidget(parent),ui(newUi::Widget){......
  • 【学习日志】Java基本数据类型的自动装箱和拆箱
    //测试代码publicstaticvoidmain(String[]args){Integera=1;Integerb=2;Integerc=3;Integerd=3;Integ......
  • QT5笔记: 16. 时间和定时器的常用功能
    例子#ifndefWIDGET_H#defineWIDGET_H#include<QTime>#include<QTimer>#include<QWidget>QT_BEGIN_NAMESPACEnamespaceUi{classWidget;}QT_END_NAM......
  • QT5笔记:10. 容器类的迭代
    1.这里指的是Java类型的迭代器,即使用方式和Java中一致代器的使用例子(适用于可读可写迭代器)QList<QString>list;//声明容器类list<<"A"<<"B"<<"C"<<"D";//......
  • QT5笔记:12. 字符串和数值之间的转换
    字符串与进制转换的例子/***@briefWidget::on_btnCalcHex_clicked从界面上获取十六进制字符串,然后转为十进制和二进制字符串写回界面*/voidWidget::on_btnCal......