并查集

codeforces 892E 可撤销并查集

蹲街弑〆低调 提交于 2019-12-04 20:17:51
https://codeforces.com/problemset/problem/892/E 把询问离线,分步回答. #include<bits/stdc++.h> #define ll long long #define rep(ii,a,b) for(int ii=a;ii<=b;++ii) #define per(ii,a,b) for(int ii=b;ii>=a;--ii) #define forn(i,x,g,e) for(int i=g[x];i;i=e[i].next) #define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0) #define ull unsigned long long #define fi first #define se second #define mp make_pair #define pii pair<int,int> #define all(x) x.begin(),x.end() #define show(x) cout<<#x<<"="<<x<<endl #define show2(x,y) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<endl #define show3(x,y,z) cout<<#x<<"="<<x<<" "<<#y<

模板 - 数据结构 - 并查集

百般思念 提交于 2019-12-04 17:55:40
Codeforces Round #600 里面有用到这个,但是真的重新打浪费时间。 不需要什么按秩合并,浪费空间多此一举,让那个合并的常数大了不少。但是循环还是有必要的,比递归快很多。 struct DisjointSetUnion { static const int MAXN = 200000; int n, fa[MAXN + 5]; void Init(int _n) { n = _n; for(int i = 1; i <= n; i++) fa[i] = i; } int Find(int u) { int r = fa[u]; while(fa[r] != r) r = fa[r]; int t; while(fa[u] != r) { t = fa[u]; fa[u] = r; u = t; } return r; } bool Merge(int u, int v) { int fu = Find(u), fv = Find(v); if(fu == fv) return false; else { fa[v] = fu; return true; } } } dsu; 来源: https://www.cnblogs.com/KisekiPurin2019/p/11876274.html

并查集

早过忘川 提交于 2019-12-04 16:43:43
并查集概念 并查集是一种用来管理元素分组情况的数据结构。并查集可以高效地进行如下操作。 查询元素a和元素b是否属于同一组。 合并元素a和元素b所在的组。 模板 1234567891011121314151617181920212223242526272829303132333435 class {private: vector<int> parent, rank;public: DisjointSets(int num) { parent = vector<int>(num, 0); rank = vector<int>(num, 0); for (int i = 0; i < num; i++) { parent[i] = i; } } int find(int p) { if(p == parent[p]) return p; parent[p] = find(parent[p]); return parent[p]; } void unionTwo(int p, int q) { int rootP = find(p); int rootQ = find(q); if (rootP == rootQ) return; if (rank[rootQ] > rank[rootP]) { parent[rootP] = rootQ; }else { parent[rootQ] =

0x40 数据结构进阶

孤人 提交于 2019-12-04 13:20:01
0x41 并查集 定义 在计算机科学中,并查集是一种树型的数据结构,用于处理一些不交集的合并及查询问题。有一个联合-查找算法定义了两个用于此数据结构的操作: \(Find\) :确定元素属于哪一个子集。它可以被用来确定两个元素是否属于同一子集。 \(Union\) :将两个子集合并成同一个集合。 由于支持这两种操作,一个不相交集也常被称为联合-查找数据结构或合并-查找集合。其他的重要方法, \(MakeSet\) ,用于创建单元素集合。有了这些方法,许多经典的划分问题可以被解决。 为了更加精确的定义这些方法,需要定义如何表示集合。一种常用的策略是为每个集合选定一个固定的元素,称为代表,以表示整个集合。接着, \(Find(x)\) 返回 \(x\) 所属集合的代表,而 $Union $使用两个集合的代表作为参数。 路径压缩与按秩合并 这是两个并查集常用的优化 当我们在寻找祖先时,一旦元素多且来,并查集就会退化成单次 \(O(n)\) 的算法,为了解决这一问题我们可以在寻找祖先的过程中直接将子节点连在祖先上,这样可以大大降低复杂度,均摊复杂度是 \(O(log(n))\) 的 按秩合并也是常见的优化方法,“秩”的定义很广泛,举个例子,在不路径压缩的情况下,常见的情况是把子树的深度定义为秩 无论如何定义通常情况是把“秩”储存在根节点,合并的过程中把秩小的根节点插到根大的根节点上

并查集判环

本秂侑毒 提交于 2019-12-04 06:07:29
int find(int x) { int r=x; while(pre[r]!=r) { r=pre[r]; } } void join(int x,int y) { int a=find(x); int b=find(y); if(a!=b) pre[a]=b; else if(a==b)//这里是重点 sign=-1;//用来标记已经是环了 } 为什么根一样就是环呢? 看个例子: 1 2 2 3 3 1 假如如上三个点,每组数字用线段连接起来,首先看第一组,因为初始化后都指向父亲节点,所以join时根节点随意组合,然后第二组同理,但是当程序运行第三组数据的时候发现他们的根是一样的,这是因为在没有第三行输入之前前两组已经形成了联系,所以填上了最后一组之后;相当于添加了一条线,就是环 。 来源: https://www.cnblogs.com/aprincess/p/11834992.html

【刷题】【二分图】【并查集】

橙三吉。 提交于 2019-12-04 05:34:36
题目: lxhgww最近迷上了一款游戏,在游戏里,他拥有很多的装备,每种装备都有2个属性,这些属性的值用[1,10000]之间的数表示。当他使用某种装备时,他只能使用该装备的某一个属性。并且每种装备最多只能使用一次。 游戏进行到最后,lxhgww遇到了终极boss,这个终极boss很奇怪,攻击他的装备所使用的属性值必须从1开始连续递增地攻击,才能对boss产生伤害。也就是说一开始的时候,lxhgww只能使用某个属性值为1的装备攻击boss,然后只能使用某个属性值为2的装备攻击boss,然后只能使用某个属性值为3的装备攻击boss……以此类推。 现在lxhgww想知道他最多能连续攻击boss多少次? 【1】二分图 属性是左集合,武器是右集合,然后二分图 复杂度O(n*n) 【2】并查集 (https://blog.csdn.net/zp1ng/article/details/79006544) 把每个属性当作一个点,武器是连边。 当一个节点数是n的联通块是一棵树时,只有n-1个属性能被满足;当联通快不是一棵树时,所有的属性都能被满足。 所以把每个联通快用并查集维护,当联通块是一棵树的时候让块内最大的属性值不能满足。最后暴力查结果就可以了。 #include<iostream> #include<cstdlib> #include<cstdio> #include<cstring>

种类并查集的一些理解

≡放荡痞女 提交于 2019-12-04 01:48:30
之前暑假集训的时候学了一下并查集,但是那个时候完全搞不懂种类并查集(带权并查集)的路径压缩以及两个节点关系的合并。 现在有重新学习了一下,算是对种类并查集有了一些粗浅的理解了吧。 附上一个写的很好的博主链接: https://blog.csdn.net/c0de4fun/article/details/7318642 关于路径压缩: int Find(int x) { if(x == father[x]) return x; int temp = father[x]; father[x] = Find(father[x]); rela[x] = (rela[x] + rela[temp]) % 3; return father[x]; } 其实之前我就不是很懂,为什么要设一个temp存储father[x]的值,不应该像下面的代码这样的吗。(现在想想我好蠢。。 int Find(int x) { if(x == father[x]) return x; father[x] = Find(father[x]); rela[x] = (rela[x] + rela[father[x]]) % 3; return father[x]; } 但是father[x]跟Find(father[x])的值有可能是不一样的。 而路径更新一定是当前节点与根节点的关系

P3367 【模板】并查集

被刻印的时光 ゝ 提交于 2019-12-04 00:21:06
题目描述 如题,现在有一个并查集,你需要完成合并和查询操作。 输入格式 第一行包含两个整数N、M,表示共有N个元素和M个操作。 接下来M行,每行包含三个整数Zi、Xi、Yi 当Zi=1时,将Xi与Yi所在的集合合并 当Zi=2时,输出Xi与Yi是否在同一集合内,是的话输出Y;否则话输出N 输出格式 如上,对于每一个Zi=2的操作,都有一行输出,每行包含一个大写字母,为Y或者N 输入输出样例 输入 #1 复制 4 7 2 1 2 1 1 2 2 1 2 1 3 4 2 1 4 1 2 3 2 1 4 输出 #1 复制 N Y N Y 说明/提示 时空限制:1000ms,128M 数据规模: 对于30%的数据,N<=10,M<=20; 对于70%的数据,N<=100,M<=1000; 对于100%的数据,N<=10000,M<=200000。 #include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<queue> using namespace std; int n,m,z,x,y,fa[10005]; int find(int x) { if (x==fa[x]) return x; return fa[x]=find(fa[x]); } int

UVA11987 带删除并查集

有些话、适合烂在心里 提交于 2019-12-03 22:48:04
1~n,n个数,初始每个数独自作为一个集合,然后进行m次操作。 操作有三种: 1 p q :把 p 所在的集合合并到 q 所在的集合 2 p q :把 p 从 p 的集合中拿出,放到 q 的集合里 3 p :输出 p 所在的集合的元素个数和元素之和 Sample Input 5 7 1 1 2 2 3 4 1 3 5 3 4 2 4 1 3 4 3 3 Sample Output 3 12 3 7 2 8 #include <iostream> #include <cstdio> #include <string> #include <cstring> #include <cmath> #include <algorithm> #include <queue> #include <map> #include <set> #include <stack> #include <vector> #define Twhile() int T;scanf("%d",&T);while(T--) #define clc(a,b) memset(a,b,sizeof(a)) #define fora(i,a,b) for(i=a;i<b;i++) #define fors(i,a,b) for(i=a;i>b;i--) #define fora2(i,a,b) for(i=a;i<=b;i++)

奇技淫巧

巧了我就是萌 提交于 2019-12-03 17:34:52
一、点权转边权:建一个超级源点,连接每个节点,边权为该点的点权,跑一边最短路就可以求出点权加路径长的最小值了。 二、用带权并查集来判断m个操作是否正确:若 \(l-1\) 与 \(r\) 是否在一个并查集里,若不在,就合并;若在,就判断是否满足条件。 三、 \(x\bigoplus x=0,0\bigoplus x=x\) 四、贪心策略假如不正确,先考虑是否可以反悔。 五、求区间内的互异个数:1.带修改:树套树或带修莫队(看数据范围);2.不带修改:主席树(在线, \(O(n\log n)\) ),莫队(离线, \(O(n \sqrt n)\) ) 六、求区间第k大:树套树或主席树 七、求区间异或信息:可持久化0-1Trie树或线性基 八、维护区间信息时,假如不会,可以用分块敲个部分分 来源: https://www.cnblogs.com/nth-element/p/11805126.html