【学习笔记】并查集应用

news/2024/9/22 19:24:47/文章来源:https://www.cnblogs.com/FlyPancake/p/18333279

【学习笔记】并查集应用

以 NOI 2001 食物链 为例の两种并查集用法。

题目大意:

规定每只动物有且仅有三种可能的种类 \(A、B、C\)\(A\) 会吃 \(B\)\(B\) 会吃 \(C\)\(C\) 会吃 \(A\)

给定 \(N\) 只动物,\(K\) 个语句。每个语句有如下两种可能的表达:

  1. 1 X Y 表示动物 \(X\) 与动物 \(Y\) 是同类。

  2. 2 X Y 表示动物 \(X\)\(Y\)

每个语句可能是真话也可能是假话,每个语句是假话有三种可能:

  1. \(X\)\(Y\)\(N\) 大。

  2. 表达为 \(X\)\(X\)

  3. 当前的话与前面的某些真的话冲突。

请求出 \(K\) 个语句里假话的总数。

种类并查集(扩展域并查集)

先推一个讲解。

并查集能维护连通性、传递性,通俗地说,亲戚的亲戚是亲戚。

然而当我们需要维护一些对立关系,比如敌人的敌人是朋友时,正常的并查集就很难满足我们的需求。

这时,种类并查集就诞生了。

同个种类的并查集中合并,表达他们是朋友这个含义。

不同种类的并查集中合并,表达他们是敌人这个含义。


此题关系有三类(\(A、B、C\)),所以我们考虑建立 3 倍大小的并查集。其中 \(1 \sim n\) 表示种类 \(A\)\(n+1 \sim 2n\) 表示种类 \(B\)\(2n+1 \sim 3n\) 表示种类 \(C\)

如果两只动物 \(x\)\(y\) 是同类,那么就将 \(A_x\)\(A_y\)\(B_x\)\(B_y\)\(C_x\)\(C_y\) 各并入一个集合内。

如果两只动物 \(x\)\(y\),那么就将 \(A_x\)\(B_y\)\(B_x\)\(C_y\)\(C_x\)\(A_y\) 各并入一个集合内。

此时如果要表示动物 \(x\) 吃动物 \(y\),就说明 \(A_x\)\(B_y\) 在同一集合中,根据对称性,其它的也一样,所以判断时只需要判一组。

  • \(x\)\(y\) 同类与 \(x\)\(y\)\(y\)\(x\) 矛盾。
  • \(x\)\(y\)\(x\)\(y\) 同类或 \(y\)\(x\) 矛盾。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 5e4+5;int fa[N*3];int find(int x){if(fa[x] == x) return x;return fa[x] = find(fa[x]);
}int main(){ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);int n, k, ans = 0; cin>>n>>k;for(int i=1; i<=n*3; i++)fa[i] = i;while(k--){int op, x, y; cin>>op>>x>>y;if(x==y&&op==2 || x>n || y>n){ans++;continue;}if(op==1){if(find(x)==find(y+n) || find(y)==find(x+n)){ans++;continue;}fa[find(x)] = fa[find(y)];fa[find(x+n)] = fa[find(y+n)];fa[find(x+n+n)] = fa[find(y+n+n)];} else if(op==2){if(find(x)==find(y) || find(y)==find(x+n)){ans++;continue;}fa[find(x)] = fa[find(y+n)];fa[find(x+n)] = fa[find(y+n+n)];fa[find(x+n+n)] = fa[find(y)];}}cout<<ans;return 0;
}

带权并查集

每个点与其集合的根都有权重,以此来表达关系。

以此题为例,0 代表 \(x\)\(fa_x\) 同类,1 代表 \(x\)\(fa_x\),2 代表 \(x\)\(fa_x\) 吃。

重点在于如何更新权值和判断关系。权值更新肯定伴随并查集的更新。在下面的图中就如向量一般计算。

查找(路径压缩)

知道 \(x\) 与其根 \(fa[x]\) 的关系,\(fa[x]\) 与其根 \(fa[fa[x]]\) 的关系,可以推出 \(x\)\(fa[fa[x]]\) 的关系。

注意这里要先更新 \(fa[x]\) 的权值(先 find(fa[x])),在更新 \(x\) 的权值(得先存下 \(fa[x]\),不然 \(fa[x]\) 会变)。

\[rel[x \rightarrow rt] = rel[x \rightarrow fa]+rel[fa \rightarrow rt] \]

find.png

合并

知道 \(x\)\(fa[x]\) 的关系,\(y\)\(fa[y]\) 的关系,以及 \(x\)\(y\) 之间的关系,就可以知道 \(fa[x]\)\(fa[y]\) 的关系。

注意是 \(fa[x]\) 并到 \(fa[y]\) 上还是 \(fa[y]\) 并到 \(fa[x]\) 上。以下是 \(fa[x]\) 并到 \(fa[y]\) 上。

\[rel[fa[x]] = rel[y]-rel[x]+rel[x \rightarrow y] \]

merge.png

判断关系(是否矛盾)

知道 \(x、y\) 与根的关系,就能推出 \(x\)\(y\) 的关系。(此时 \(x\)\(y\) 已经在同一个集合内)

\[rel[x \rightarrow y] = rel[x]-rel[y] \]

check.png

以上操作取模时注意减法,显然此题模数为 \(3\)

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 50005;int fa[N], rel[N];
// relation 存与根的关系
// 0--同类,1--能吃,2--被吃
const int p = 3;
int n, k, ans;void init(){for(int i=1; i<=n; i++){fa[i] = i;rel[i] = 0;// 初始化跟自己的关系是同类}
}int find(int x){if(fa[x] == x) return x;// 知道 x 与 fa[x] 的关系,fa[x] 与根的关系,可以推出 x 与根的关系// rel[x->rt] = rel[x->fa]+rel[fa->rt]int f = fa[x];fa[x] = find(fa[x]);rel[x] = (rel[x]+rel[f])%p;// 必须得分开写,因为原来的 fa[x] 与根的关系会在 find(fa[x]) 的时候更新return fa[x];
}void merge(int u, int v, int r){// U与rtU的关系,V与rtV的关系,以及UV之间的关系,就可以知道rtU和rtV的关系。// rtU 并到 rtV 上// rel[ru] = rel[v]-rel[u]+rel[u->v]int ru = find(u), rv = find(v);if(ru != rv){fa[ru] = rv;rel[ru] = (rel[v]-rel[u]+r+p)%p;}
}bool check(int x, int y, int r){if(x>n || y>n) return false; // 不能比 n 大if(x==y && r==1) return false; // 不能吃自己if(find(x)==find(y)){// 知道x、y与根的关系,就能推出 x 与 y 的关系// rel[x->y] = rel[x]-rel[y]return r == (rel[x]-rel[y]+p)%p;}return true;// 还没明确的关系就是可行的
}int main(){ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);cin>>n>>k;init();while(k--){int op, x, y; cin>>op>>x>>y;if(check(x, y, op-1)){merge(x, y, op-1);} else{ans++;}}cout<<ans;return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/774231.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《NET CLR via C#》---第三章(运行时解析类型引用)

"运行时"解析类型引用首先在"C:\Users\LH89\source\repos"目录下,新建Console1工程(C#控制台)实现简单的代码,并编译为程序集(假定名为Program.exe)using System;public class Program {static void Main(string[] args){Console.WriteLine("Hell…

2024“钉耙编程”中国大学生算法设计超级联赛(3) 1005 数论

题意:分析: 远看数论题,实则是道数据结构。 记 \(f_{i}\) 表示 \(r_{k}=i\) 的方案数,\(g_{i}\) 表示 \(l_{1}=i\) 的方案数,那么运用简单容斥,可得: \[ans_{x} = (\sum_{i=1}^{n} f_{i}) - ((\sum_{i=1}^{x-1}f_{i})+1) \times ((\sum_{i=x+1}^{n}g_{i})+1)+1 \]先考虑…

金蝶云星空历史库存信息批量计算生成

一、业务背景今天是2024年07月30日,系统2024年01月01日启用,导入初始库存。 二、需求背景需要快速查询库存组织=供应链中心下,某仓库某物料的库存数。后面还需要按照过去时间范围查询每一天的库存量以监控变化。 三、参考《库存汇总表》《库存余额》《库存账龄分析》《物料收…

使用 useSeoMeta 进行 SEO 配置

title: 使用 useSeoMeta 进行 SEO 配置 date: 2024/7/30 updated: 2024/7/30 author: cmdragon excerpt: 摘要:本文介绍了Nuxt3中的useSeoMeta组合函数,用于简化和优化网站的SEO配置。通过这个工具,开发者可以在Nuxt3项目中方便地设置页面元标签,包括标题、描述以及Open …

史上最便宜的服务器

史上最便宜的服务器:免费空间 10米永久使用,不过空间容量只有100M 提示:不是广告,有时候需要用的时候找老半天,干脆做个随笔记录

格式举例

文章目录a markdown unordered list which will be replaced with the toc, * 号前面和后面需要有个空格。文本 这是一个段落,我要把它设置为蓝色,只需在前面的标签上嵌入style属性即可,style用于内联css。 to bold text, use <strong>. to italicize text, use <e…

代码随想录二刷(链表章节)

代码随想录二刷(链表章节)链表就是通过指针串联在一起的线性结构,每个节点都是由一个数据域和指针域(存放下一个节点的指针)。 双链表就是每个节点中既有指向前一个节点的,也有指向后一个节点的。循环链表就是把头和尾连起来。性能分析如下:下面来看下链表的具体题目: Leet…

[Redis]哨兵

这次聊聊,Redis 的哨兵机制。为什么要有哨兵机制? 在 Redis 的主从架构中,由于主从模式是读写分离的,如果主节点(master)挂了,那么将没有主节点来服务客户端的写操作请求,也没有主节点给从节点(slave)进行数据同步了。这时如果要恢复服务的话,需要人工介入,选择一个…

一屏掌控物流风云:数据大屏开启高效运营时代

在日新月异的数字化浪潮中,物流行业正以前所未有的速度迈向智能化、高效化的新纪元。作为这一变革的核心驱动力之一,山海鲸可视化搭建的物流订单数据监控大屏以其强大的信息整合能力、实时的数据可视化展示以及精准的业务决策支持,成为了现代物流企业不可或缺的智慧中枢。想…

ofd轻阅读超大文件优化方案

本人使用Typescript开发了一款ofd 阅读器,参见文章《ofd轻阅读》。web端实现阅读功能有两种方案: ofd转svg;使用h5 canvas。 两种方案各有优劣,本人采用了canvas方案, 劣势:开发难点较大,需要处理更多的细节(比如:文字选中)。 优势:对细节掌控能力更强,能满足用户更…

笔记:从Aurora 8b/10b 到Aurora 64b/66b (一):Aurora 8b/10b

参考: https://www.xilinx.com/products/intellectual-property/aurora8b10b.html#documentation https://docs.amd.com/r/en-US/pg046-aurora-8b10b https://docs.amd.com/v/u/en-US/aurora_8b10b_ds797 https://mp.weixin.qq.com/s/gT4QUgvoFF6UI0PAhfEPvQ 补丁: Aurora 系…

矩阵指数的定义

人就像是被蒙着眼推磨的驴子,生活就像一条鞭子;当鞭子抽到你背上时,你就只能一直往前走,虽然连你也不知道要走到什么时候为止,便一直这么坚持着。