并查集

并查集总结

╄→尐↘猪︶ㄣ 提交于 2019-11-30 03:09:57
并查集 一、定义 并查集 是一种树型的数据结构,用于处理一些不相交 集合 (Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。 集合定义方法: “代表元法”,即 每个集合选择一个固定的元素,作为整个集合的“代表” 。 二、基本操作 Find —— 查询一个元素属于哪一个集合 Merge —— 把两个集合合并成一个大集合 代码示例: 三、路径压缩与按秩合并 ——并查集的 奇技淫巧 ①路径压缩 Get时, 将访问过的节点直接指向树根。 均摊复杂度 代码示例: int find(int x) { return x == fa[x] ? fa[x] : fa[x] = find(fa[x]); }    按秩合并 “秩”:树的深度(未路径压缩) / 集合大小 。均摊复杂度 将”秩“记录在 “代表元素” , 合并时, 将“秩”较小的树根 作为 “秩”较大的树根的子节点。 代码示例: void unionn(int x, int y){//按大小合并 int u=Get(x), v=Get(y); if(u != v) { if(size[u]<size[v]) f[u]=v, size[v]+=size[u];//按大小合并每次要更新大小 else f[v]=u, size[u]+=size[v]; } } void unionn(int x, int y){/

P3043 [USACO12JAN]牛联盟(并查集+数学)

。_饼干妹妹 提交于 2019-11-30 02:21:06
(m<n<=1e5,有重边) 题目表述有问题..... 给定一张图(不一定联通),每条边可以选择连接的两个点之一,剩余的点可以自己成对,问方案数。 一开始是真的被吓到了....觉得可写性极低的一题..... 但是两个结论如果推出来的话就蛮好的了 solution: 一开始想:对于每个块进行大小统计,然后组合数乘在一起。但是,有点麻烦: 有环的情况:对于一个联通块有环,那么就会有n个点,n条边,那就意味着会有一个联通块只有一个单独的点。单独考虑环块(下统称环块) 看看这个三元环(误),先确定第一条边选左或右两个点,如果第一个边确定了自己的选择,那么它就会占用下一条边的一个选择的权利,也就是: 如果确定了一条边,就可以确定整个环上的方案数:2 如果不是裸环的环块呢? 看这个: 同上,确定了环上的两个,链的一端就会被占用,也就是说: 只要是环块,对答案的贡献就是2! (伟大的发现) 考虑无环的情况: 一共n个点,n-1条边(无向图,不能有环就是树),一共有n种方案。感性证明一下(不知道该怎么理性): 最后一个点不被选,而这个不被选的点一共有n种情况,这就是n种方案。 最后根据乘法原理,把所有的方案数乘起来就是ans。 综上结论,题目就变成了:给定一张图,判断联通块大小,找联通块里的环,统计答案 联通块?我有并查集! 找环?我有tarjan! (旁边两位z姓大佬给我闷头两巴掌

[题解]POJ_1417_(并查集\\背包

只谈情不闲聊 提交于 2019-11-30 02:14:05
https://blog.csdn.net/qq_34731703/article/details/54603652 1.转化:yes表示双方同类,否则不同类(真的蔡 题目变为有一些集合,内部分两个集合,现在要从每个大集合里选出一个小集合使得选出的这些集合大小之和恰好为p1,且只能有一种方案(这样才能确定每个元素是谁 所以不用可行性dp,我们记录方案数,设$f[i][j]$为前$i$个拼成$j$的方案数,转移显然 但是要输出方案,由于要求从小到大输出编号,而且每个小集合里有很多元素,所以不记录从哪转移,而是看$f[i-1][j-size]$方案数是否为1,如果是1的话肯定是从这转移来的,然后暴力枚举所有元素,选出属于这个集合的即可 #include<iostream> #include<algorithm> #include<cstdio> #include<cstring> using namespace std; const int maxn=1009; int n,p1,p2; int fa[maxn],d[maxn],w[2][maxn],v[maxn],p[maxn]; inline int find(int x){ if(x==fa[x])return x; int rt=find(fa[x]); d[x]^=d[fa[x]]; return fa[x]=rt; }

(圆相交+并查集)

混江龙づ霸主 提交于 2019-11-30 02:03:29
题目链接: https://codeforces.com/gym/101915/problem/J 思路:将所有相交的圆用并查集维护看做一个整体,然后枚举每个整体的左边界和右边界,判断能不能同时覆盖整个路。 AC代码: 1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int maxn = 1e5 + 5; 5 int n; 6 struct circle{ 7 ll x, y, r; 8 }; 9 circle c[maxn]; 10 bool intercircle(circle a, circle b) 11 { 12 ll d = (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y); 13 ll r = a.r + b.r; 14 if(d <= r * r) return true; 15 else return false; 16 } 17 int far[maxn], L[maxn], R[maxn]; 18 int find(int x) 19 { 20 if(far[x] == x) return x; 21 else return far[x] = find(far[x]); 22 } 23

BZOJ4668: 冷战 (并查集 + LCA)

倖福魔咒の 提交于 2019-11-29 18:51:53
题意:动态给点连边 询问两个点之间最早是在第几个操作连起来的 题解:因为并查集按秩合并 秩最高是logn的 所以我们可以考虑把秩看作深度 跑LCA #include <bits/stdc++.h> using namespace std; const int MAXN = 5e5 + 5; int n, m, cnt; int fa[MAXN]; int id[MAXN]; int zhi[MAXN]; int find(int x) { if(fa[x]) return find(fa[x]); else return x; } int ffind(int x) { if(fa[x]) return ffind(fa[x]) + 1; else return 0; } void add(int x, int y, int z) { int fx = find(x); int fy = find(y); if(fx != fy) { if(zhi[fx] < zhi[fy]) { fa[fx] = fy; id[fx] = z; } else fa[fy] = fx, id[fy] = z; if(zhi[fx] == zhi[fy]) zhi[fx]++; } } int query(int x, int y) { int fx = find(x); int fy = find

树 dfs暴力判环 题意转化

大憨熊 提交于 2019-11-29 17:11:18
以后还是要多做题啊 这一道题我把题目想的太简单了 用并查集做了一波 但是忘了一种情况 就是同一个树上可能会有环 这就不太对了 而且还不要忘了 一棵树的根节点是一个自环 也就是说这一题的答案就是 环的数量-1(有一棵树的根节点不用改) 就是dfs一波就行了 下一次读清楚题! 来源: https://www.cnblogs.com/Tidoblogs/p/11523993.html

并查集模板hdu-1213

馋奶兔 提交于 2019-11-29 15:06:39
目录 例题:hdu 1213 1、合并的优化 2、查询的优化——路径的压缩 3、优化完成的代码 例题:hdu 1213 //#include <bits/stdc++.h> #include <iostream> #include <stack> #include <string> #include <queue> #include <stack> #include <set> #include <list> #include <map> #include <algorithm> #include <string.h> using namespace std; const int MAXN = 1005; int s[MAXN]; void init_set(){ for(int i = 1; i <= MAXN; i++) s[i] = i; } int find_set(int x){ return x = s[x]? x: find_set(s[x]); } void union_set(int x, int y){ x = find_set(x); y = find_set(y); if(x != y) s[x] = s[y]; } int main(){ int t, n, m, x, y; cin >> t; while(t--){ cin >> n >> m;

可持久化并查集(草稿)

此生再无相见时 提交于 2019-11-29 13:37:59
#include<bits/stdc++.h> #define fi first #define se second #define INF 0x3f3f3f3f #define LNF 0x3f3f3f3f3f3f3f3f #define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0) #define pqueue priority_queue #define NEW(a,b) memset(a,b,sizeof(a)) const double pi=4.0*atan(1.0); const double e=exp(1.0); const int maxn=4e5+8; typedef long long LL; typedef unsigned long long ULL; const LL mod=998244353; const ULL base=1e7+7; const int maxp=26+5; using namespace std; struct Pus{ struct node{ int l,r,fa,dep; }tr[maxn*30]; int root[maxn*30]; int cnt; void build(int &rt,int l,int r){ rt=++cnt; if(l==r)

bzoj2733 永无乡 (并查集+线段树合并)

浪尽此生 提交于 2019-11-29 12:17:10
问题描述 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示。某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛。如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连 通的。现在有两种操作:B x y 表示在岛 x 与岛 y 之间修建一座新桥。Q x k 表示询问当前与岛 x连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k 小的岛是哪 座,请你输出那个岛的编号。 Input 输入文件第一行是用空格隔开的两个正整数 n 和 m,分别 表示岛的个数以及一开始存在的桥数。接下来的一行是用空格隔开的 n 个数,依次描述从岛 1 到岛 n 的重要度排名。随后的 m 行每行是用空格隔开的两个正整数 ai 和 bi,表示一开始就存 在一座连接岛 ai 和岛 bi 的桥。后面剩下的部分描述操作,该部分的第一行是一个正整数 q, 表示一共有 q 个操作,接下来的 q 行依次描述每个操作,操作的格式如上所述,以大写字母 Q 或B 开始,后面跟两个不超过 n 的正整数,字母与数字以及两个数字之间用空格隔开。 对于 20%的数据 n≤1000,q≤1000 对于 100%的数据 n≤100000,m≤n,q≤300000 Output 对于每个

并查集[union-find sets]

好久不见. 提交于 2019-11-29 10:12:30
创建 1 const int maxn=5000; 2 int father[maxn]; 3 void make(){ 4 for(int i=0;i<maxn;i++) //初始化 5 father[i]=i; //令每个元素的父亲都是元素本身 6 } 查找 1 int find(int x){ 2 if(x!=father[x]) //若父亲不为本身,则寻找元素x的祖先 3 father[x]=find(father[x]); //通过寻找元素x父亲的父亲找到祖先 4 return father[x]; //返回元素x的祖先 5 } 合并 1 void merge(int x,int y){ 2 x=find(x); //寻找x的祖先 3 y=find(y); //寻找y的祖先 4 if(x==y) //若x和y为同一祖先,return 5 return; 6 father[x]=y;//else令x的祖先为y,x集合从属于y集合 7 } 来源: https://www.cnblogs.com/kohano/p/11512931.html