LCT 维护边双 / 点双的模板
用 \(\text{LCT}\) 维护边双的做法是:加入一条非树边时,将这段树上路径合并为一个点代表这个边双,具体实现用并查集合并点,在 \(\text{Splay}\) 与 \(\text{Access}\) 的过程中对辅助树上父亲做路径压缩。
用 \(\text{LCT}\) 维护点双的做法是:加入一条非树边时,将这段树上路径全部砍断,新建一个点代表这个点双,将原来那些点向新点连虚边。
实现方法:直接用 \(\text{Splay}\) 与 \(\text{Access}\) 提取路径并且 \(\text{DFS}\) 遍历这个路径就可以了。
代码是维护路径桥与割点的个数。 洛谷链接
#include<bits/stdc++.h>
using namespace std;
const int N = 200005;
int n,q,lst;
struct bcj
{
int f[N];
void init()
{
for(int i=1; i<=n; i++)
f[i]=i;
}
int find(int x)
{
return f[x]==x?x:f[x]=find(f[x]);
}
}G;
namespace LCT
{
const int maxn = N;
int ch[maxn][2],fa[maxn],s[maxn],rev[maxn];
#define lc(x) (ch[x][0])
#define rc(x) (ch[x][1])
bcj W;
#define F(x) (W.find(x))
int g(int x)
{
return rc(fa[x]) == x;
}
int nrt(int x)
{
return lc(fa[x]) == x || rc(fa[x]) == x;
}
void pushup(int x)
{
s[x] = s[lc(x)] + s[rc(x)] + 1;
}
void pushdown(int x)
{
if(rev[x])
{
swap(lc(lc(x)), rc(lc(x)));
rev[lc(x)] ^= 1;
swap(lc(rc(x)), rc(rc(x)));
rev[rc(x)] ^= 1;
rev[x] ^= 1;
}
}
void rot(int x)
{
int y = fa[x], v = g(x);
if(nrt(y))
ch[fa[y]][g(y)] = x;
fa[x] = fa[y];
ch[y][v] = ch[x][!v];
fa[ch[x][!v]] = y;
ch[x][!v] = y;
fa[y] = x;
pushup(y);
}
void splay(int x)
{
x = F(x);
int y; vector<int>v;
for(y = x; nrt(y); y = fa[y] = F(fa[y]))
v.push_back(y);
for(pushdown(y); !v.empty(); v.pop_back())
pushdown(v.back());
for(y = fa[x]; nrt(x); rot(x), y = fa[x] = F(fa[x]))
if(nrt(y))
rot(g(x) ^ g(y) ? x : y);
pushup(x);
}
void access(int x)
{
x = F(x);
for(int y = 0; x; y = x, x = fa[x] = F(fa[x]))
{
splay(x);
rc(x) = y;
}
}
void makeroot(int x)
{
x = F(x);
access(x);
splay(x);
swap(lc(x), rc(x));
rev[x] ^= 1;
}
void link(int x, int y)
{
x = F(x); y = F(y);
makeroot(x);
access(y);
splay(y);
fa[x] = y;
}
void dfs_merge(int x, int root)
{
W.f[x] = root;
if(lc(x)) dfs_merge(lc(x), root), lc(x) = 0;
if(rc(x)) dfs_merge(rc(x), root), rc(x) = 0;
}
void zip(int x, int y)
{
x = F(x); y = F(y);
makeroot(x);
access(y);
splay(y);
dfs_merge(y, y);
pushup(y);
}
int query(int x, int y)
{
x = F(x); y = F(y);
makeroot(x);
access(y);
splay(y);
return s[y] - 1;
}
#undef lc
#undef rc
#undef F
}
namespace RST
{
const int maxn = N << 1;
int cnt,ch[maxn][2],fa[maxn],s[maxn],rev[maxn];
#define lc(x) (ch[x][0])
#define rc(x) (ch[x][1])
int g(int x)
{
return rc(fa[x]) == x;
}
int nrt(int x)
{
return lc(fa[x]) == x || rc(fa[x]) == x;
}
void pushup(int x)
{
s[x] = s[lc(x)] + s[rc(x)] + (x <= n);
}
void pushdown(int x)
{
if(rev[x])
{
swap(lc(lc(x)), rc(lc(x)));
rev[lc(x)] ^= 1;
swap(lc(rc(x)), rc(rc(x)));
rev[rc(x)] ^= 1;
rev[x] ^= 1;
}
}
void rot(int x)
{
int y = fa[x], v = g(x);
if(nrt(y))
ch[fa[y]][g(y)] = x;
fa[x] = fa[y];
ch[y][v] = ch[x][!v];
fa[ch[x][!v]] = y;
ch[x][!v] = y;
fa[y] = x;
pushup(y);
}
void splay(int x)
{
int y; vector<int>v;
for(y = x; nrt(y); y = fa[y])
v.push_back(y);
for(pushdown(y); !v.empty(); v.pop_back())
pushdown(v.back());
for(y = fa[x]; nrt(x); rot(x), y = fa[x])
if(nrt(y))
rot(g(x) ^ g(y) ? x : y);
pushup(x);
}
void access(int x)
{
for(int y = 0; x; y = x, x = fa[x])
{
splay(x);
rc(x) = y;
}
}
void makeroot(int x)
{
access(x);
splay(x);
swap(lc(x), rc(x));
rev[x] ^= 1;
}
void link(int x, int y)
{
makeroot(x);
access(y);
splay(y);
fa[x] = y;
}
void dfs_merge(int x, int root)
{
fa[x] = root;
if(lc(x)) dfs_merge(lc(x), root), lc(x) = 0;
if(rc(x)) dfs_merge(rc(x), root), rc(x) = 0;
pushup(x);
}
void zip(int x, int y)
{
makeroot(x);
access(y);
splay(y);
dfs_merge(y, ++cnt);
pushup(cnt);
}
int query(int x, int y)
{
makeroot(x);
access(y);
splay(y);
return s[y];
}
#undef lc
#undef rc
}
int main()
{
scanf("%d %d", &n, &q);
G.init();
LCT::W.init();
RST::cnt = n;
for(int i = 1, opt, x, y; i <= q; i ++)
{
scanf("%d %d %d", &opt, &x, &y);
x = x ^ lst; y = y ^ lst;
if(opt == 1)
{
if(G.find(x) ^ G.find(y))
LCT::link(x, y), RST::link(x, y), G.f[G.find(x)] = G.find(y);
else
LCT::zip(x, y), RST::zip(x, y);
}
if(opt == 2)
{
if(G.find(x) ^ G.find(y))
puts("-1");
else
printf("%d\n", lst = LCT::query(x, y));
}
if(opt == 3)
{
if(G.find(x) ^ G.find(y))
puts("-1");
else
printf("%d\n", lst = RST::query(x, y));
}
}
return 0;
}
LCT 维护边双 / 点双的模板的更多相关文章
- 关于双端队列 deque 模板 && 滑动窗口 (自出)
嗯... deque 即为双端队列,是c++语言中STL库中提供的一个东西,其功能比队列更强大,可以从队列的头与尾进行操作... 但是它的操作与队列十分相似,详见代码1: 1 #include < ...
- bzoj2959: 长跑 LCT+并查集+边双联通
题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=2959 题解 调了半天,终于调完了. 显然题目要求是求出目前从 \(A\) 到 \(B\) 的可 ...
- LCT总结——概念篇+洛谷P3690[模板]Link Cut Tree(动态树)(LCT,Splay)
为了优化体验(其实是强迫症),蒟蒻把总结拆成了两篇,方便不同学习阶段的Dalao们切换. LCT总结--应用篇戳这里 概念.性质简述 首先介绍一下链剖分的概念(感谢laofu的讲课) 链剖分,是指一类 ...
- 高可用Mysql架构_Mycat集群部署(HAProxy + 两台Mycat+Mysql双主双从)
既然大家都知道了Mysql分布式在大型网站架构中的作用,在这里就不再阐述.本片博客文章是基于我曾经搭建过的一个Mysql集群基础上实现的,实现过双主热备.读写分离.分库分表. 博客链接:http:// ...
- windows2003服务器双线双IP双网卡设置方法
双线双ip很好,网通用户访问网通线路,电信用户访问电信线路.但很多人会选用导入静态路由表,这个办法看似完美,其实问题很多. 1.电信用户如果被解析到网通的ip上,服务器根据路由表会返回电信线路,但用户 ...
- 【洛谷1501】[国家集训队] Tree II(LCT维护懒惰标记)
点此看题面 大致题意: 有一棵初始边权全为\(1\)的树,四种操作:将两点间路径边权都加上一个数,删一条边.加一条新边,将两点间路径边权都加上一个数,询问两点间路径权值和. 序列版 这道题有一个序列版 ...
- 【洛谷3950】部落冲突(LCT维护连通性)
点此看题面 大致题意: 给你一棵树,\(3\)种操作:连一条边,删一条边,询问两点是否联通. \(LCT\)维护连通性 有一道类似的题目:[BZOJ2049][SDOI2008] Cave 洞穴勘测. ...
- 【BZOJ2049】[SDOI2008] Cave 洞穴勘测(LCT维护连通性)
点此看题面 大致题意: 有\(n\)个洞穴,\(3\)种操作:连一条边,删一条边,询问两点是否联通. \(LCT\)维护连通性 这道题应该是\(LCT\)动态维护连通性的一道模板题. 考虑将\(x\) ...
- Docker 部署 RocketMQ 双主双从模式( 版本v4.7.0)
文章转载自:http://www.mydlq.club/article/96/ 系统环境: 系统版本:CentOS 7.8 RocketMQ 版本:4.7.0 Docker 版本:19.03.13 一 ...
随机推荐
- kmp算法散记
1. https://blog.csdn.net/abcjennifer/article/details/5794547 #include<bits/stdc++.h> using nam ...
- Android开发 UI布局
Android开发 UI布局一.线性布局LinearLayout 什么是线性布局? 其实呢,线性布局就是把所有的孩子摆在同一条线上 <?xml version="1.0" e ...
- 【css】用css巧妙实现移动端横向滑动展示功能
前言:记得以前处理移动端横向滑动展示都是去用js去解决的,要用js进行蛮多处理,要算li的宽度,然后还要用js设置ul盒子的宽度,又要设置最大滑动距离,最小滑动距离等等.......但是现在发现用cs ...
- Linux平台下C++使用JsonCPP解析Json字符串
JsonCPP安装 安装 scons 下载地址: http://sourceforge.net/projects/scons/files/scons/2.1.0/scons-2.1.0.tar.gz/ ...
- AcWing 789. 数的范围 二分+模板
https://www.acwing.com/problem/content/791/ #include<bits/stdc++.h> using namespace std; ; int ...
- awk从放弃到入门(3):awk变量
一.变量概述 对于awk来说"变量"又分为"内置变量" 和 "自定义变量" , "输入分隔符FS"和"输出分隔 ...
- eclipse报错:unable to install breakpoint in .......due to missing line number attributes
报错信息如下: 解决方案方案1.把断点都干掉,再启动.应该是代码更新后,断点位置没有代码了或位置改变了. 方案2.在Eclipse - Preferences - Java - Complier 下 ...
- JarvisOJ - Writeup(5.31更新)
此篇用来记录我在jarivsOJ上的一些题解,只给解题思路,不放flag Misc 0x01 You Need Python(300) 题目有两个文件,一个py文件,另一个是经过编码的key 文件ke ...
- JS高级---案例:随机小方块 (贪吃蛇的食物部分)
案例:随机小方块 产生随机数对象,自调用构造函数 产生小方块对象,自调用构造函数,里面存放: 食物的构造函数 给原型对象添加方法:初始化小方块的显示的效果及位置---显示地图上 给原型对象添加方法,产 ...
- 第二十九篇 玩转数据结构——线段树(Segment Tree)
1.. 线段树引入 线段树也称为区间树 为什么要使用线段树:对于某些问题,我们只关心区间(线段) 经典的线段树问题:区间染色,有一面长度为n的墙,每次选择一段墙进行染色(染色允许覆盖),问 ...