ZR并查集专题
ZR并查集专题
并查集,作为一个基础算法,对于初学者来说,下面的代码是维护连通性的利器
return fa[x] == x ? x : fa[x] = getf(fa[x]);
所以,但是这对并查集的理解还仅仅停留在维护连通性上,如果能够对并查集的理解继续深刻,就会接触到维护元素之间的关系
主要表现在边带权和扩展域的用法
我们将从一道例题引出这两种写法
noi2001食物链(Luogu2024)
扩展域:
这种关系具有传递性,同时要维护多种关系的时候,一般的合并很难做到这一点
这时候我们要引出扩展域
所谓扩展域,顾名思义,就是将并查集维护的东西进行扩展
此题中,我们不但要维护同类.还要维护吃与被吃
那么我们就把节点\(x\)拆成三个点\(x_{self}\)同类域,\(x_{eat}\)捕食域以及\(x_{enemy}\)天敌域
每次读进来一个同类操作\(x,y\)
我们如果发现\(x_{self}\)和\(y_{eat}\)或者\(x_{self}\)和\(y_{enemy}\)在同一集合
那么这次操作显然不合法
否则
将x,y三个集合一一合并
同理
如果是读入的捕食关系
如果\(x_{self }\)与\(y_{self}\)或者\(x_{enemy}\)与\(y_{self}\)在同一集合
那么不合法
否则,x吃的就是y,x的天敌就是y吃的,x是y的天敌一一合并
边带权
边带权并查集好恶心啊
这是这道题的第二种做法,不需要对并查集进行扩展,我们利用带权并查集解决这个问题
首先,我们并查集维护一下距离
\(x,y\)的距离定义为\(dis_x - dis_y\)我们定义这个值在模\(3\)意义下为\(0\)则代表\(x,y\)是同类,为\(1\)代表\(x\)被\(y\)吃,为\(2\)则代表\(x\)吃\(y\)
我们接下来考虑这种情况
默认大家已经会带权并查集的基本写法
对于\(1\)询问如果\(x,y\)已经在同一集合,那么他们的关系我们可以简单计算出来,并且判断是否合法,如果\(x,y\)不在统一集合,也就是我们要确定他们的关系

也就是说
我们已经知道\(a,b\)现在要把\(f_y\)接到\(f_x\)上
也就是要满足\(a-b-c \equiv 0 \pmod {3}\)
这个还是比较简单的方程
注意一定是把\(f_y\)接到\(f_x\)上
因为如果把\(f_x\)接到\(f_y\)上就不是这个方程了
同理,吃的操作我们也可以这么做
式子变一下就好了
如果理解了上面这东西,这里应该改也不难了
上面可以说是并查集的进一步扩展,能够维护敌对或者同类等多样的关系
那如果我们再加深一下对于并查集的理解,请看下面的题
ZR855
给定一棵 \(n\) 个点的树,每个点有点权 \(w_i\),定义一条链 \((a,b)\) 的权值 \(V(a,b)\) 为:这条链上的所有点的最大点权
现在你需要对于每个点 \(x\) 求:所有点到他的链的最大点权之和,也就是 \(\sum_{i = 1}^ n V(i,x)\)
长得非常淀粉质的样子,但这确确实实是一道并查集的题目
首先,观察题目性质,我们可以发现一条边的边权我们可以定义为他链接的两个点的点权的最大值
那么就有一个比较套路的想法,把所有的边按照边权排序之后一条一条地加入到树中,并用并查集维护每一次操作的贡献,如果这道题目要求的是求所有路径的权值之和,那么想到这里就完成了.但是如果要求对于给个数,都要输出到其他点的距离之和,我们就要把贡献具体到点上,而不是统一计算
接下来考虑,如何计算这个东西,考虑一下并查集合并的过程

\(v\)合并到\(u\)上的时候,发现\(u\)子树和\(v\)子树中都多了\(w(u,v)\)的贡献,我们就类似线段的tag每次在\(u\)处记录下\(v\)的贡献,\(v\)同理,但是\(v\)合并到\(u\)之后,之前的\(tag\)是不能共享的,那我们就把这一部分减去
请注意:如果是用这种方法,一定要只按秩合并 ,只按秩合并 ,只按秩合并!!!!!
因为路径压缩维护这个东西会非常麻烦
我们按秩合并能够保证最终并查集的高度不会超过\(\log\),对于查询操作,我们可以暴力的从他的位置向上跳,一路统计贡献.
特别的,如果查询都是在合并完成之后,我们可以最终dfs整棵树得到某个节点的值
那如果我们非要路径压缩怎么办?
想一想,刚才路径压缩为什么维护tag会特别麻烦?
因为压缩之后tag会重复计数,主要原因是根导致的
那么我们就借鉴克鲁斯卡尔重构树的思想
每次合并的时候我们建立一个虚点,然后把两个点都连在虚点上,这样我们就可以 肆无 忌 惮 的路径压缩了,而且由于\(v\)不是\(u\)的儿子,所以也不会有tag多余的部分,只需要简单的统计贡献即可
给出这道题的两种做法
路径压缩:
#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<vector>
#include<ctime>
#include<cmath>
#include<set>
#include<map>
#define LL long long
#define pii pair<int,int>
#define mk make_pair
#define fi first
#define se second
using namespace std;
const int N = 1e6 + 3;
struct edge{
int from;
int to;
int data;
}e[N << 1];
int head[N],fa[N],size[N];
int w[N];LL val[N];
int n;
inline int read(){
int v = 0,c = 1;char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') c = -1;
ch = getchar();
}
while(isdigit(ch)){
v = v * 10 + ch - 48;
ch = getchar();
}
return v * c;
}
inline bool cmp(edge x,edge y){
return x.data < y.data;
}
inline int getf(int x){
// printf("%d %d\n",x,fa[x]);
if(fa[x] == x) return x;
int f = getf(fa[x]);
val[x] += val[fa[x]];
fa[x] = f;
return fa[x];
}
int main(){
n = read();
for(int i = 1;i < n;++i){
e[i].from = read();
e[i].to = i + 1;
}
for(int i = 1;i <= n;++i) fa[i] = i,w[i] = read(),size[i] = 1;
for(int i = n + 1;i <= 2 * n;++i) fa[i] = i,size[i] = 0;
for(int i = 1;i < n;++i) e[i].data = max(w[e[i].from],w[e[i].to]);
sort(e + 1,e + n,cmp);
// for(int i = 1;i < n;++i) printf("%d %d %lld\n",e[i].from,e[i].to,e[i].data);
int cnt = n;
for(int i = 1;i < n;++i){
int x = getf(e[i].from),y = getf(e[i].to);
val[x] += 1ll * size[y] * e[i].data;
val[y] += 1ll * size[x] * e[i].data;
size[++cnt] = size[x] + size[y];
fa[x] = cnt;
fa[y] = cnt;
}
for(int i = 1;i <= n;++i){
getf(i);
printf("%lld ",val[i] + w[i]);
}
return 0;
}
按秩合并:
#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<vector>
#define LL long long
using namespace std;
const int N = 4e5 + 311;
int n,m,tot;
LL ans[N];
int fa[N],size[N];
LL w[N];
vector <int> G[N];
struct edge{
int from;
int to;
LL data;
}e[N << 1];
inline int getf(int x){
return x == fa[x] ? x : getf(fa[x]);
}
inline bool cmp(edge x,edge y){
return x.data < y.data;
}
inline int read(){
int v = 0,c = 1;char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') c = -1;
ch = getchar();
}
while(isdigit(ch)){
v = v * 10 + ch - 48;
ch = getchar();
}
return v * c;
}
inline void dfs(int x,int f){
ans[x] += ans[f];
for(int i = 0;i < (int)G[x].size();++i){
int y = G[x][i];
if(y == f) continue;
dfs(y,x);
}
}
int main(){
n = read();
for(int i = 2;i <= n;++i){
int x = read();
e[++tot] = (edge){i,x,0};
}
for(int i = 1;i <= n;++i) w[i] = read();
for(int i = 1;i <= n + 13;++i) fa[i] = i,size[i] = 1;
for(int i = 1;i <= tot;++i) e[i].data = max(w[e[i].from],w[e[i].to]);
// for(int i = 1;i <= tot;++i){
// printf("%d %d %lld\n",e[i].from,e[i].to,e[i].data);
// }
sort(e + 1,e + tot + 1,cmp);
for(int i = 1;i <= tot;++i){
int x = getf(e[i].from),y = getf(e[i].to);
if(size[x] > size[y]) swap(x,y);
if(x == y) continue;
// printf("%d %d %lld %lld\n",x,y,ans[x],ans[y]);
G[y].push_back(x);
ans[y] += e[i].data * size[x];
ans[x] += e[i].data * size[y];
ans[x] -= ans[y];
size[y] += size[x];
fa[x] = y;
}
dfs(getf(1),0);
for(int i = 1;i <= n;++i) printf("%lld ",ans[i] + w[i]);
return 0;
}
总结:如果时间复杂度不是特别卡,那么建虚点的做法还是非常优秀的,实测虽然点数多以一倍,但是跑的并不劣于按秩合并的做法
ZR并查集专题的更多相关文章
- Leetcode之并查集专题-765. 情侣牵手(Couples Holding Hands)
Leetcode之并查集专题-765. 情侣牵手(Couples Holding Hands) N 对情侣坐在连续排列的 2N 个座位上,想要牵到对方的手. 计算最少交换座位的次数,以便每对情侣可以并 ...
- Leetcode之并查集专题-684. 冗余连接(Redundant Connection)
Leetcode之并查集专题-684. 冗余连接(Redundant Connection) 在本问题中, 树指的是一个连通且无环的无向图. 输入一个图,该图由一个有着N个节点 (节点值不重复1, 2 ...
- 【并查集专题】【HDU】
PS:做到第四题才发现 2,3题的路径压缩等于没写 How Many Tables Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65 ...
- 并查集专题: HDU1232畅通工程
畅通工程 Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submi ...
- 并查集专辑 (poj1182食物链,hdu3038, poj1733, poj1984, zoj3261)
并查集专题训练地址,注册登录了才能看到题目 并查集是一个树形的数据结构, 可以用来处理集合的问题, 也可以用来维护动态连通性,或者元素之间关系的传递(关系必须具有传递性才能有并查集来维护,因为并查集 ...
- poj1417(带权并查集+背包DP+路径回溯)
题目链接:http://poj.org/problem;jsessionid=8C1721AF1C7E94E125535692CDB6216C?id=1417 题意:有p1个天使,p2个恶魔,天使只说 ...
- hdu 1598 find the most comfortable road (并查集)
find the most comfortable road Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K ...
- poj1733 Parity game[带权并查集or扩展域]
地址 连通性判定问题.(具体参考lyd并查集专题该题的转化方法,反正我菜我没想出来).转化后就是一个经典的并查集问题了. 带权:要求两点奇偶性不同,即连边权为1,否则为0,压缩路径时不断异或,可以通过 ...
- [ An Ac a Day ^_^ ] [kuangbin带你飞]专题五 并查集 POJ 2236 Wireless Network
题意: 一次地震震坏了所有网点 现在开始修复它们 有N个点 距离为d的网点可以进行通信 O p 代表p点已经修复 S p q 代表询问p q之间是否能够通信 思路: 基础并查集 每次修复一个点重新 ...
随机推荐
- 【JZOJ4920】【NOIP2017提高组模拟12.10】降雷皇
题目描述 降雷皇哈蒙很喜欢雷电,他想找到神奇的电光. 哈蒙有n条导线排成一排,每条导线有一个电阻值,神奇的电光只能从一根导线传到电阻比它大的上面,而且必须从左边向右传导,当然导线不必是连续的. 哈蒙想 ...
- struts1之工作原理
版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/zkn_CS_DN_2013/article/details/34452341 1.初始化:strut ...
- Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第二十一章:环境光遮蔽(AMBIENT OCCLUSION)
原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第二十一章:环境光遮蔽(AMBIENT OCCLUSION) 学习目标 ...
- retailMall-vuedemo1
//home.vue <template> <div class="home"> <div class="top-info"> ...
- Python2.7下,调用subprocess启动子进程,读取子进程标准输出若干问题
1:如果调用的子进程也是一个python脚本,则subprocess.Popen中的bufsize=1无效果.也就是说,即使设置了bufsize=1表示进行行缓冲,子进程如果不显示调用sys.stdo ...
- Sublime text 3 如何格式化HTML/css/js代码
Sublime Text 3 安装Package Control 原来Subl3安装Package Control很麻烦,现在简单的方法来了 一.简单的安装方法 使用Ctrl+`快捷键或者通过Vi ...
- CSDN编程挑战——《-3+1》
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/user_longling/article/details/24674033 -3+1 题目详情: 有 ...
- 十年磨一剑,王坚自研的MaxCompute如何解决世界级算力难题
摘要: 2009年这项关于大数据的技术长征开始.王坚带队,目标是自研大数据计算平台MaxCompute统一阿里巴巴内部的数据和大数据计算体系. 大数据时代,随着企业数据规模的急剧增长,传统软件已无法承 ...
- 2018-11-26-WPF-通过-DrawingContext-DrawImage-绘制图片
title author date CreateTime categories WPF 通过 DrawingContext DrawImage 绘制图片 lindexi 2018-11-26 16:1 ...
- Streamy障碍一:大批量条目