长安大学ACM协会讲课记录之『基础数据结构—并查集』
讲课之前
我们不妨先考虑下傻瓜式合并,加入我们开一个数组表示某一个集合,那么在集合合并的时候,需要把每一个元素移动至对应集合,合并复杂度O(n),查找复杂度O(n)。
我们能优化合并么?当然可以,用链表或者STL的list。
由于不支持随机存取,那么查找的复杂度还是O(n)。怎么优化查找,那就用并查集了
概述
并查集顾名思义就是有“合并集合”和“查找集合”两种操作的关于数据结构的一种算法。
操作
初始化:
1 | void init() |
查找:
1 | int find(int x) |
合并:
1 | void unite(int x,int y) |
判同:
1 | bool same(int x,int y) |
复杂度
复杂度很玄,算法导论有证明。路径压缩好写,但是大多数人不写启发式合并,可能会被卡。。
裸并查套路
并查集大约有以下特征但不局限于此。
维护区域信息
KRUSKAL
最小生成树算法,无向图的环,无向图连通分量,线段覆盖,区域联通等维护关系 拆点,
lCA
,虚拟图,等价类等破坏关系 拆边,逆向并查集等//正难则反
例题
L1
TOJ2469 Friends
题目描述:有n个人,m对朋友关系,朋友关系对称且可传递,求有几个朋友圈。
分析:事实上是求一个无向图的连通分量数,并查集轻松搞定。
TOJ3294 Building Blcok
题目描述:一开始有n个Block,分别放置在地面上,有P个操作,操作有两种类型:
M a b如果a,b没有在一个Block组里,把包含a Block的Block组放在包含b的Block组之上。
C a 输出有多少个Block被压在了a之下。
类似题目 排队询问间隔
划分等价类
scau 1138 代码等式 //题目链接实在搞不到
Description
一个代码等式就是形如 $ x{1}x{2} \dot x{i} = y{1}y{2} \dot y{j} $ ,这里$ x{i} $和 $ y{j} $是二进制的数字(0或1)或者是一个变量(如英语中的小写字母)。每一个变量都是一个有固定长度的二进制代码。
例如:
a,b,c,d,e是变且它们的长度分别是4,2,4,4,2。考虑等式:1bad1 = acbe ,这个等式共有16组解。现要求任给一个等式,计算一共有多少组解。(变量最多26个,长度和不超过10000)
输入格式
第一行数N为变量个数;第二行N个数,为每个变量的位数;第三行为一个等式
输出格式
输出解的个数,无解输出0
输入样例
5
4 2 4 4 2
1bad1=acbe
输出样例
16
描述
1920年的芝加哥,出现了一群强盗。如果两个强盗遇上了,那么他们要么是朋友,要么是敌人。而且有一点是肯定的,就是A的朋友的朋友是A的朋友;A的敌人的敌人也是A的朋友。
两个强盗是同一伙的当且仅当他们是朋友。现在给你一些关于强盗们的信息,问你至多有多少个强盗团伙。
输入格式
输入的第一行为N(2<=N<=1000),表示强盗的个数(从1编号到N)。第二行M(1<=M<=5000),表示信息条数。以下M行,每行可能是F p q或是E p q,分别表示p和q是朋友,或是敌人。假设输入不会产生矛盾
输出格式
输出只有一行,表示最大可能的团伙数。
朋友很好处理。
很明显一点,一个人的敌人一定是一个集合。
设e[x] 为x的敌人集合,如果出现p,q是敌人 那么就把e[p]与q合并 e[q]与p合并
合并次数为T 那么ans为N-T
改变询问内容 变为维护关系
维护区域
可以搞一些BFS求联通水域//
线段覆盖问题//
维护关系
POJ 食物链 把团伙关系加强版