并查集

News Distribution题解 (并查集)

被刻印的时光 ゝ 提交于 2020-03-11 14:56:56
题目链接:https://codeforces.com/group/5yyKg9gx7m/contest/270506/problem/F 思路 很明显是个并查集,不过在并查集的基础上要统计每个节点的所有子结点的数量,最后遍历每个结点,输出其根节点的所有子节点数就ok。 # include <iostream> # include <cstdio> # include <cmath> # include <algorithm> # include <string> # include <map> # include <cstring> # include <climits> # include <vector> # include <queue> # include <stack> # define ll long long using namespace std ; int N , M ; int fa [ 500001 ] ; //记录每个节点的当前根节点 int co [ 500001 ] ; //记录每个节点的子节点数+1(因为要包括自己) int find ( int x ) //寻找x的根节点 { if ( x == fa [ x ] ) return x ; else return fa [ x ] = find ( fa [ x ] ) ; } int main (

AcWing 算法基础 并查集

梦想与她 提交于 2020-03-11 13:00:08
并查集 并查集解决的问题 将两个集合合并 询问两个元素是否在一个集合中 基本原理 用树的形式来维护每个集合,树根的编号就是整个集合的编号,每个节点存储他的父节点,p[x]表示x的父节点 问题1,如何判断树根:if(p[x] == x); 问题2,如何求x集合的编号: while(p[x] != x) x = p[x]; 问题3,如何合并两个集合,px是x集合的编号,py是y的集合编号,令p[x]=y。 问题2优化:某个节点找到根节点之后,把整个路径上的点直接指向根节点。 例题 AcWing 836 合并集合 一共有n个数,编号是1~n,最开始每个数各自在一个集合中。 现在要进行m个操作,操作共有两种: “M a b”,将编号为a和b的两个数所在的集合合并,如果两个数已经在同一个集合中,则忽略这个操作; “Q a b”,询问编号为a和b的两个数是否在同一个集合中; import java . util . * ; class UnionSet { private final int N = 100010 ; private int [ ] p = new int [ N ] ; public UnionSet ( ) { for ( int i = 0 ; i < N ; i ++ ) { p [ i ] = i ; } } // 找到该节点的root节点 public int

并查集

我与影子孤独终老i 提交于 2020-03-11 02:25:00
# include <bits/stdc++.h> # define INF 0x3f3f3f3f3f3f3f3f # define mod 1000000007 # define IOS ios::sync_with_stdio(false) # define endl '\n' using namespace std ; typedef long long ll ; const int maxn = 1e5 + 10 ; int f [ maxn ] ; int n , ans ; void init ( ) { //数组初始化(每个人是自己的组先) for ( int i = 1 ; i <= n ; ++ i ) { f [ i ] = i ; } return ; } int Get_Father ( int v ) { if ( f [ v ] == v ) //如果祖先是自己 return v ; else { f [ v ] = Get_Father ( f [ v ] ) ; //否则继续寻找并把途径的所有祖先的祖先设置为最年长的祖先() return f [ v ] ; //类似于把自己和父亲和爷爷都设置为曾祖父的儿子 } } void Search ( ) { //查找有几个祖先 for ( int i = 1 ; i <= n ; ++ i ) { if (

The 2017 ACM-ICPC Asia Beijing Regional Contest

☆樱花仙子☆ 提交于 2020-03-07 07:53:52
地址 Rank Solved A B C D E F G H I J 51/384 4/10 . . Ø . O O . O Ø O O : 当场通过 Ø : 赛后通过 . : 尚未通过 A Domains unsolved B K-Dimensional Foil unsolved C Graph upsolved by chelly chelly's solution 很显然的思路就是莫队+并查集 但众所周知并查集可以支持可撤销,但不是很好支持可持久化 于是就可以用上回滚莫队的套路了,回滚莫队可以把一般莫队的删除操作变成撤销操作,复杂度不改变 于是这题用回滚莫队+可撤销并查集即可解决 注意回滚莫队的时候,处理右半边的点的时候,不能连向左半边的点,否则rollback的时候跨越中间的边并没有撤销 D Chinese Checkers unsolved E Cats and Fish solved by chelly chelly's solution F Secret Poems solved by chelly chelly's solution G Liaoning Ship’s Voyage unsolved H Puzzle Game solved by chelly chelly's solution I Colored Nodes upsolved by chelly

POJ 1182 食物链(带权并查集经典例题)

爷,独闯天下 提交于 2020-03-06 23:58:52
题目链接 动物王国中有三类动物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 只有一个整数,表示假话的数目。 1.题目就不再阐述,那么怎么知道是用带权并查集来写呢?如果是刚接触并查集碰到这道题,那么一定很懵,这并不是一个直接的“集合关系”,而是相互联系的,我们已知某几个数之间的联系后

蓝桥杯——发现环(并查集+DFS)

妖精的绣舞 提交于 2020-03-05 21:01:27
题目: 输入输出: 分析: 根据题目可知只会多出一条边,那么只有可能出现一个环,每当输入两个点的时候使用并查集判断两个点是否有相同的祖先,如果没有,将两个点合并,如果两个点已经具有共同的祖先,说明此时这条边构成了环,那么此时可以将这两个点作为环的起点和终点,使用DFS搜索这一条路径,所经过的点即为环上的点。 代码: # include <iostream> # include <cstring> # include <cstdio> # include <vector> # include <algorithm> using namespace std ; const int MAXN = 100005 ; int root [ MAXN ] , rnk [ MAXN ] , num [ MAXN ] ; bool vis [ MAXN ] , found ; vector < int > vec [ MAXN ] ; int n , ind ; int Getr ( int x ) { if ( root [ x ] == x ) return x ; else return root [ x ] = Getr ( root [ x ] ) ; } void Union ( int x , int y ) { if ( rnk [ x ] > rnk [ y ] ) root [

Codeforces843A 思维(伪并查集

a 夏天 提交于 2020-03-05 08:25:07
题意:给你一串n个数的序列 你可以把这个序列分成任意个序列 且要满足分成的每个子序列重新排序后再放回原来的对应位置 原序列有序 思路:就是模拟一下整个过程,(和找并查集有点类似) 首先因为数范围很大,总数量不大,所以离散化,因为没有相等的 所以不用去重。 自然而然的,离散化之后的序列就是1~n的n个数 所以我们要做的就是: 对于当前的数组,扫一遍,对于每个元素: 若这个元素的值等于它的坐标,这个元素单独分一个序列 如果这个元素的值不等于它的坐标,(而这个值最后是要排到当前值的坐标的),所以令p=a[a[p]] 然后直到p==a[i]就停下 把这个过程中的所有元素放成一组 最后还要给vector排个序 下面是ac代码: # include <algorithm> # include <iostream> # include <cstdio> # include <cstring> # include <string> # include <cmath> # include <queue> # define ll long long # define inf 0x3f3f3f3f # define sd(a) scanf("%d",&a) # define sdd(a,b) scanf("%d%d",&a,&b) # define cl(a,b) memset(a,b,sizeof(a

200岛屿数量(并查集未写)

不想你离开。 提交于 2020-03-04 19:54:28
题目描述 给定一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。 示例 1: 输入: 11110 11010 11000 00000 输出: 1 示例 2: 输入: 11000 11000 00100 00011 输出: 3 思路分析 DFS:感染算法。遍历整个数组,元素为1,则count++,调用感染函数将1变为2,并感染上下左右,直到感染停止,这样能得到一块岛屿。 并查集(未写):矩阵被分为几个部分,逐个进行单个计算,最后汇总。汇总时,用并查集做相邻矩阵边界统计。首先将边界处的数据封装为结点加入并查集中,合并同一个岛上的结点,在分析边界时,查边界两边的1是否在一个集合上,如果不在就union两个结点,岛数目-1,否则继续下一行的两点。 代码实现 public int numIslands ( char [ ] [ ] grid ) { if ( grid == null || grid . length == 0 || grid [ 0 ] . length == 0 ) { return 0 ; } int count = 0 ; for ( int i = 0 ; i < grid . length ; i ++ ) { for ( int j =

并查集算法详解

隐身守侯 提交于 2020-03-03 06:57:57
更好的阅读体验 并查集算法详解 算法详解 维护类型 身为一个数据结构,我们的并查集,它的维护对象是我们的关注点. 并查集适合维护具有 非常强烈的传递性质,或者是连通集合性质. 性质详解 传递性质 传递性,也就是具有传递效应的性质,比如说A传递给B一个性质或者条件,让B 同样拥有 了这个性质或者条件,那么这就是我们所说的传递性. 连通集合性质 连通集合性,和数学概念上的集合定义是差不多的, 比如说A和B同属一个集合,B和C同属一个集合,那么A,B,C 都属于同一个集合 .这就是我们所谓的连通集合性质. 算法步骤 一般来说数据结构算法,没有所谓的算法步骤,但是却有 半确定的模块功能. 初始化操作 数据结构的初始化,通常都是有一个固定的模块,并查集也不例外.对于并查集而言,它的初始化,就是 指向的父亲节点. 我们可以想象集合就是一个小圈子,而没一个小圈子都得有一个圈主,那么显然所以人都是围绕着圈主行动的. 比如说Acwing这个大圈子中,yxc总裁就是我们的红太阳,圈主大人. 同属于一个集合的人们,显然每一个人的指向目标,显然都是这个 圈子的圈主 . 然而刚开始的时候,显然Acwing的成员们,在没有加入Acwing的时候, 基本上都是素不相识的 .因此呢,我们所有人肯定是都是属于自己的一个 单人小圈子 .自己显然就是 自己这个小圈子的圈主 . 综上所述,我们刚开始,每一个人的指向数组

【并查集】亲戚

雨燕双飞 提交于 2020-03-03 03:39:05
题目描述 或许你并不知道,你的某个朋友是你的亲戚。他可能是你的曾祖父的外公的女婿的外甥女的表姐的孙子。如果能得到完整的家谱,判断两个人是否亲戚应该是可行的,但如果两个人的最近公共祖先与他们相隔好几代,使得家谱十分庞大,那么检验亲戚关系实非人力所能及。在这种情况下,最好的帮手就是计算机。为了将问题简化,你将得到一些亲戚关系的信息,如Marry和Tom是亲戚,Tom和Ben是亲戚,等等。从这些信息中,你可以推出Marry和Ben是亲戚。请写一个程序,对于我们的关于亲戚关系的提问,以最快的速度给出答案。 输入 输入由两部分组成。 第一部分以N,M开始。N为问题涉及的人的个数,M表示已经知道M对亲戚关1<=N,M<=100000,接下来M行,每行有两个数ai, bi,表示已知ai和bi是亲戚。这些人的编号为1,2,3,…, N。接下来输入一个整数P(1<=P<=100000),表示有P次询问,接下来P行,每行为ci, di,表示询问ci和di是否为亲戚。 输出 若ci和di为亲戚,则输出“Yes”,否则输出“No”。 题解: 本题涉及到并查集方面的知识,不懂的可以去翻翻资料。 首先,我们就用一个f数组来记录每个点的亲戚,因为每个人一开始的亲戚就是自己,那么就“for i:=1 to n do f[i]:=i;”然后,每次输入一个关系链,就进行一次并查集。然后查询的时候就用并查集来查询