并查集

边带权并查集 +离散化 acwing 239

雨燕双飞 提交于 2020-02-06 12:45:46
题意: 一个01序列s,有一些关系l r odd,或者是l r even,分别代表在[l,r]区间内,有奇数个1,有偶数个1. 问最早出现冲突的下标-1. 思路: 用前缀和的思想考虑,l到r的区间,1的个数的奇偶性,就是1~(l-1)的1的个数的奇偶性和1~r的1的个数的奇偶性的关系。 如果sum[l-1]和sum[r]奇偶性相同,那么l到r的1的个数就是偶数,不同就是奇数。 用并查集去维护每个点和祖先的关系,d数组代表和祖先之间的1的个数的奇偶性,偶数为0,奇数为1。 运算规则和异或一样,偶数和偶数为偶数,奇数和奇数为偶数,奇数和偶数为奇数。 合并的时候,x,和y合并。fa[fx]=fy;要计算d[fx]。 x~y的路径:x~fx fx~fy fy~y,x和y的关系a[i].ans=d[x]^d[y]^d[fx]。 d[fx]=d[x]^d[y]^a[i].ans; #pragma warning(disable:4996) #include<algorithm> #include<cmath> #include<iostream> #include<cstdio> #include<cstring> #include<stack> #include<vector> #include<map> #include<string> #include<set> #include

并查集

岁酱吖の 提交于 2020-02-06 02:16:47
1.定义 并查集是一种维护集合的数据结构,它的名字中“并”、“査”、“集”分别取自Union(合并)、Find(査找)、Set(集合)这3个单词。也就是说,并查集支持下面两个操作: ①合并:合并两个集合。 ②査找:判断两个元素是否在一个集合。 那么并査集是用什么实现的呢?其实就是用一个数组: int father[N]; 其中fahter[i]表示元素i的父亲结点,而父亲结点本身也是这个集合内的元素(1≤i≤N)。例如father[1]=2就表示元素1的父亲结点是元素2,以这种父系关系来表示元素所属的集合。另外,如果 father[i]=i,则说明元素i是该集合的根结点,但对同一个集合来说只存在一个根结点,且将其作为所属集合的标识。 如下图所示,father数组的情况如下: 2.基本操作 总体来说,并查集的使用需要先初始化father数组,然后再根据需要进行查找或合并的操作。 (1).初始化 一开始,每个元素都是独立的一个集合,因此需要令所有 father[i]等于i: for(int i=1;i<=N;i++){ father[i] = i; //令father[i]为-1也可,此处father[i]=i为例 } (2).查找 由于规定同一个集合中只存在一个根结点,因此查找操作就是对给定的结点寻找其根结点的过程。实现的方式可以是递推或是递归,但是其思路都是一样的

边带权并查集 acwing 238

随声附和 提交于 2020-02-06 01:00:46
题意: 有n列战舰,有两种操作,第一种,把一列战舰放在另一列的后面,也就是合并操作,第二种是查询操作,问两个战舰是不是在同一列,如果是的话,就输出它们之间隔了多少个战舰,如果不是就输出-1. 思路: 用一个数组表示是不是在一个集合里,然后用另一个数组d表示每一列的大小,还有一个数组siz表示边的权值。 d是到祖先的距离,siz是这列的大小。如果在同一列里面,就直接输出d[x]-dy[y]的绝对值减一。 #pragma warning(disable:4996) #include<algorithm> #include<cmath> #include<iostream> #include<cstdio> #include<cstring> #include<stack> #include<vector> #include<map> #include<string> #include<set> #include<queue> using namespace std; typedef long long ll; int fa[30005], d[30005], siz[30005]; int find(int x) { if (x != fa[x]) { int t = fa[x]; fa[x] = find(fa[x]); d[x] += d[t];//到祖先的距离 } return

最小生成树之克鲁斯卡尔(Kruskal)算法

百般思念 提交于 2020-02-05 16:54:56
学习最小生成树算法之前我们先来了解下 下面这些概念: 树 (Tree):如果一个无向连通图中不存在回路,则这种图称为树。 生成树 (Spanning Tree):无向连通图G的一个子图如果是一颗包含G的所有顶点的树,则该子图称为G的生成树。 生成树是连通图的极小连通子图。这里所谓极小是指:若在树中任意增加一条边,则将出现一条回路;若去掉一条边,将会使之变成非连通图。 最小生成树 (Minimum Spanning Tree,MST):或者称为最小代价树Minimum-cost Spanning Tree:对无向连通图的生成树,各边的权值总和称为生成树的权,权最小的生成树称为最小生成树。 构成生成树的准则有三条: <1> 必须只使用该网络中的边来构造最小生成树。 <2> 必须使用且仅使用n-1条边来连接网络中的n个顶点 <3> 不能使用产生回路的边。 构造最小生成树的算法主要有:克鲁斯卡尔(Kruskal)算法和普利姆(Prim)算法他们都遵循以上准则。 接下分别讨论一下这两种算法以及判定最小生成树是否唯一的方法。 克鲁斯卡尔算法 克鲁斯卡尔算法的基本思想是以边为主导地位,始终选择当前可用(所选的边不能构成回路)的最小权植边。所以Kruskal算法的第一步是给所有的边按照从小到大的顺序排序。这一步可以直接使用库函数qsort或者sort。接下来从小到大依次考察每一条边(u,v)。

leetcode 第一次做并查集的题目(#^.^#) (547、684、721、1319、399)

北城余情 提交于 2020-02-05 09:08:28
547 朋友圈 题目描述: 班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。 给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。 思路: 并查集。 并查集包括与节点大小相等的pre[]数组,find()函数,join()函数三部分。给出的矩阵为对称的邻接矩阵,如果值为1,则用join()函数将对应的两个点相连。find()函数中包含了路径压缩过程。 注意路径压缩后,顶点并不一定是该连通域所用节点的前一节点。 class Solution { public: vector<int> pre; int find(int x) { return (pre[x] == x ? x : pre[x] = find(pre[x])); } void join(int x, int y) { int prex = find(x); int prey = find(y); if(prex != prey) pre[prex] = prey; return ; } int findCircleNum(vector

POJ-1182 食物链(种类并查集)

徘徊边缘 提交于 2020-02-05 03:22:58
http://poj.org/problem?id=1182 Description 动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。 现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。 有人用两种说法对这N个动物所构成的食物链关系进行描述: 第一种说法是"1 X Y",表示X和Y是同类。 第二种说法是"2 X Y",表示X吃Y。 此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。 1) 当前的话与前面的某些真的话冲突,就是假话; 2) 当前的话中X或Y比N大,就是假话; 3) 当前的话表示X吃X,就是假话。 你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。 Input 第一行是两个整数N和K,以一个空格分隔。 以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。 若D=1,则表示X和Y是同类。 若D=2,则表示X吃Y。 Output 只有一个整数,表示假话的数目。 Sample Input 100 7 1 101 1 2 1 2 2 2 3 2 3 3 1 1 3 2 3 1 1 5 5

POJ-2492 A Bug's Life(种类并查集)

允我心安 提交于 2020-02-05 01:46:52
http://poj.org/problem?id=2492 题意: 给出一个T代表几组数据,给出一个n一个m,代表人的编号由1~n,m条命令,每条命令由两个数值组成,代表这两个人性别不同,问所有命令是否符合逻辑 两种写法: 第一种:带权并查集 1 #include <stdio.h> 2 #include <string.h> 3 #include <iostream> 4 #include <string> 5 #include <math.h> 6 #include <algorithm> 7 #include <vector> 8 #include <stack> 9 #include <queue> 10 #include <set> 11 #include <map> 12 #include <sstream> 13 const int INF=0x3f3f3f3f; 14 typedef long long LL; 15 const int mod=1e9+7; 16 const int maxn=1e5+10; 17 using namespace std; 18 19 int fa[5005]; 20 int dis[5005];//表示i和其根结点的关系,1表示异性,0表示同性 21 22 void init(int n) 23 { 24 for(int i=0

并查集(C++实现)

时光怂恿深爱的人放手 提交于 2020-02-05 00:55:22
由于我使用 并查集 来解决Kruskal算法(最小生成树),至于并查集的定义我并不是特别熟悉,看下面的并查集的实现过程应该对并查集有所了解。 并查集在Kruskal中的作用, 用来判断某两个顶点连接起来判断这棵树是否构成环。 并查集讲解。 下面是我自己用C++将并查集封装成一个类。 # include <iostream> # define max 10 using namespace std ; class Union { public : int parent [ max ] ; int rank [ max ] ; //rank[i]表示 i节点的根节点的层数 Union ( int v ) { //v为并查集大小 for ( int i = 0 ; i < v ; i ++ ) { parent [ i ] = i ; rank [ i ] = 1 ; } } int find ( int p ) { //查找某节点的根节点 if ( p >= max || p < 0 ) return - 1 ; while ( p != parent [ p ] ) { //说明已经找到根节点了 parent [ p ] = parent [ parent [ p ] ] ; p = parent [ p ] ; } return p ; } bool isConnected (

畅通工程1863(并查集)(WA的思考)

随声附和 提交于 2020-02-05 00:35:00
畅通工程 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 47870 Accepted Submission(s): 21342 Problem Description 省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可)。经过调查评估,得到的统计表中列出了有可能建设公路的若干条道路的成本。现请你编写程序,计算出全省畅通需要的最低成本。 Input 测试输入包含若干测试用例。每个测试用例的第1行给出评估的道路条数 N、村庄数目M ( < 100 );随后的 N 行对应村庄间道路的成本,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间道路的成本(也是正整数)。为简单起见,村庄从1到M编号。当N为0时,全部输入结束,相应的结果不要输出。 Output 对每个测试用例,在1行里输出全省畅通需要的最低成本。若统计数据不足以保证畅通,则输出“?”。 Sample Input 3 3 1 2 1 1 3 2 2 3 4 1 3 2 3 2 0 100 Sample Output 3 ? Source 注意一下判断有点没有联通的方法: 可以用循环(双重循环

并查集好题

可紊 提交于 2020-02-04 12:18:07
给你一个n*n的矩阵,每个格子的数代表给格子的海拔,求下一之后的积水是多少。(n<=1000) 思路:思路还是很巧妙的,维护并查集,排序以后从小到大枚举高度,然后看四周有没有之前已经访问过的或者已经flow的,就算是已经漏水的,漏水的话,这个格子也会露水,否则吧当前高度作为父亲,排序的作业是英文漏水只可能从低的地方漏水,不可能从高的地方漏水。 代码: #include<algorithm> #include<iostream> #include<stdio.h> #include<string.h> //using namespace std; const int maxn=1010; int f[maxn*maxn]; int ix[5]= {0,0,1,-1}; int iy[5]= {-1,1,0,0}; int n; inline int get(int v) { return f[v]==v?v:f[v]=get(f[v]); } inline void Merge(int v,int u) { int t1 = get(v); int t2 = get(u); f[t1] = t2; return; } struct node { int x,y; int high,num; bool flow,vi; } N[maxn*maxn]; int MAP[maxn*maxn