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 一 ...
随机推荐
- 【模板】裸SPFA
SPFA可以处理带负边权的图,可以判负环,然而SPFA容易被卡,即使加了各种优化. 队列优化的贝尔福德曼:裸SPFA //SPFA #include<bits/stdc++.h> usin ...
- MySql -- default 默认约束
常用数据库约束: 一.default 默认约束: 二.not null:非空约束,指定某列不为NULL: 三.unique:唯一约束,指定某列和几列组合的数据不能重复: 四.primary key:主 ...
- 在UTF-8页面中引入编码为GBK的JavaScript文件乱码问题了
原文地址:http://js8.in/2009/12/11/%E5%AF%B9%E5%BC%95%E7%94%A8%E5%A4%96%E9%83%A8javascript%E9%A1%B5%E9%9D ...
- Java-POJ1011-sticks
很经典的搜索题,直接爆搜会卡在连续相同长度的木棍,可以先排序,预处理该长度不行直接跳下一长度木棍的位置 但此题特殊,木棍长度小于50,我们可以直接桶排序 还有就是关于回溯的理解: 我们写的dfs为的是 ...
- python之文件复制
python文件复制操作. # -*- coding: utf-8 -*- import shutil import os # file_path = 'C:\\Users\\WT\\Desktop\ ...
- JS字符串的不可变性
js中的字符串特性->不可变性,字符串的值是不可变的 1.改变字符串中的字符 var str = "hello"; str[1] = "W"; conso ...
- arcgis计算X坐标值、Y坐标值
arcgis计算X坐标值.Y坐标值 要计算的字段上点击右键,选择计算几何,弹出计算几何对话框.可以设置属性.坐标系.单位等.
- code ELIFECYCLE 报错处理
npm ERR! code ELIFECYCLEnpm ERR! errno 1npm ERR! m-kbs-vip@1.2.12 toserver: `tua -p toserver`npm ERR ...
- 吴裕雄 python 机器学习——数据预处理标准化StandardScaler模型
from sklearn.preprocessing import StandardScaler #数据预处理标准化StandardScaler模型 def test_StandardScaler() ...
- 《一句话理解Vue核心内容》阅读笔记
Vue.js(读音 /vjuː/,类似于 view) 是一套构建用户界面的渐进式框架. 在解释什么是渐进式框架之前,有必要了解一下什么是框架 在最初的前端开发中,我们利用JS获取HTML中的DOM元素 ...