论述并查集的文章[经典] https://www.cnblogs.com/zhxmdefj/p/11117791.html 一、并查集 (1)hdu1232 城镇交通 [https://www.luogu.com.cn/problem/P1536] 是同一个题 知识点: 并查集初始化,递归查询并压缩路径,并查集合并,计算最终有多少个并查集等知识点,别忘了集合数量减1是答案。 (2)poj1611 感染的学生 知识点: .需要记录每个集合中的元素个数,如果两个集合合并,还需要同时修改最终集合的个数值。 .注意初始化时也需要将每个集合的初始值设置为1 .知道了按秩合并并不是必须要学习的,因为按路径压缩的方法也是一样可以保持集合元素个数的。按秩合并代码较长,不方便记忆,OI一般不考。 .如果使用递归可能会造成爆栈,可以使用循环的方式进行替换递归的方法,但似乎用的不多。 .递归压缩路径是在查询时进行的,按秩合并是在合并时将小树加到大树中。 二、带权并查集 .每个节点都记录的是与父节点之间的权值,那么在Find的路径压缩过程中,权值也应该做相应的更新,因为在路径压缩之前, 每个节点都是与其父节点链接着,那个Value自然也是与其父节点之间的权值 向量偏移法 路径压缩 int FindSet(int x) { if (x == parent[x]) return x; else { int t = parent[x]; //记录原父节点编号 parent[x] = FindSet(parent[x]); //父节点变为根节点,此时value[x]=父节点到根节点的权值 value[x] += value[t]; //当前节点的权值加上原本父节点的权值 return parent[x] } } 黄海解读:其实这个FindSet因为是递归,所以肯定执行多次,也就是说value[x]是要被叠加多次,比如 C->B->A三个元素 权值为1,2, 表示C到B的权值是1,B到A的权值是2。现在进行FindSet,就是查询C的祖先了,那么C就会被执行一次FindSet(C),找到了B,B也需要执行 一次FindSet(B)找到了A,A也需要执行一次FindSet(A),发现结果是自己,开始回复B,B知道了自己的祖先是A,修改自己的祖先,然后告诉 了C,C修改了自己的祖先。 其实,在上面的递归过程中,权值也会一并变化,将上面的修改一下: .在两个并查集做合并的时候,权值也要做相应的更新,因为两个并查集的根节点不同 int xRoot = FindSet(x); int yRoot = FindSet(y); if (xRoot != yRoot){ parent[xRoot] = yRoot; value[xRoot] = value[y] + s - value[x]; } 不懂看图:https://img2018.cnblogs.com/blog/1536438/201907/1536438-20190701134930241-1566925104.png (1) hdu3038 .权值的变化方法 parent[xRoot] = yRoot; value[xRoot] = value[y] + s - value[x]; .由于是区间和,需要有一点技巧,就是开闭区间,一般采用l--,然后左开右闭的方法 阅读材料: https://blog.csdn.net/qq_43591839/article/details/101348031 https://blog.csdn.net/niushuai666/article/details/6981689