并查集

奶酪(并查集)

こ雲淡風輕ζ 提交于 2019-11-27 02:41:15
链接: https://ac.nowcoder.com/acm/problem/16417 来源:牛客网 奶酪(并查集) 题目描述 现有一块大奶酪,它的高度为 h,它的长度和宽度我们可以认为是无限大的,奶酪中间有许多半径相同的球形空洞。我们可以在这块奶酪中建立空间坐标系, 在坐标系中,奶酪的下表面为 z = 0,奶酪的上表面为 z = h。 现在, 奶酪的下表面有一只小老鼠 Jerry, 它知道奶酪中所有空洞的球心所在的坐标。如果两个空洞相切或是相交,则 Jerry 可以从其中一个空洞跑到另一个空洞,特别地,如果一个空洞与下表面相切或是相交, Jerry 则可以从奶酪下表面跑进空洞; 如果一个空洞与上表面相切或是相交, Jerry 则可以从空洞跑到奶酪上表面。 位于奶酪下表面的 Jerry 想知道, 在不破坏奶酪的情况下,能否利用已有的空洞跑到奶酪的上表面去? 空间内两点 P1(x1,y1,z1) 、P2(x2,y2,z2) 的距离公式如下: 输入描述: 每个输入文件包含多组数据。 输入文件的第一行,包含一个正整数 T,代表该输入文件中所含的数据组数。 接下来是 T 组数据,每组数据的格式如下: 第一行包含三个正整数 n, h 和 r, 两个数之间以一个空格分开,分别代表奶酪中空洞的数量,奶酪的高度和空洞的半径。 接下来的 n 行,每行包含三个整数 x, y, z,

最近公共祖先(LCA)的Tarjan算法

一曲冷凌霜 提交于 2019-11-27 00:50:23
最近公共祖先(LCA)问题 LCA(T,u,v):在有根树T中,询问一个距离根最远的结点x,使得x同时为结点u、v的祖先 LCA问题可以用朴素的DFS方法解决,但是时间复杂度就很高了,这里介绍一种高级一点的解决LCA问题的Tarjan算法。 Tarjan算法是由 Robert Tarjan 在1979年发现的一种高效的离线算法,也就是说,它要首先读入所有的询问(求一次LCA叫做一次询问),然后并不一定按照原来的顺序处理这些询问。 首先需要有一些预备知识: 1.基本图论 这个就不多讲了,如果有不知道的可以随便抓一本数据结构的书恶补一下。 2.并查集 并查集其实也是很简单的东西,实现的代码都不超过10行。 这里提一下并查集的概念,并查集是一种处理元素之间等价关系的数据结构,一开始我们假设元素都是分别属于一个独立的集合里的,主要支持两种操作: 合并两个不相交集合(Union) 判断两个元素是否属于同一集合(Find) 需要知道一点,就是并查集的Find操作的时间复杂度是常数级别的。 考察树 T 中所有与结点 u 有关的询问 (u, v) 对于子树 u 中的结点 v ,满足 LCA(u, v) = u 对于子树 p1 而非子树 u 中的结点 v ,满足 LCA(u, v) = p1 对于子树 p2 而非子树 p1 中的结点 v ,满足 LCA(u, v) = p2 算法DFS有根树T

带权并查集

感情迁移 提交于 2019-11-27 00:33:16
带权并查集是一种并查集的操作,其功能与基础并查集相同,支持查询和合并操作。除此之外,带权并查集还维护了子节点与父节点之间的关系,在一个有序的集合中,某个元素到根节点的距离(例:NOI2002银河英雄传说),其操作是除了维护节点关系数组f以外,还维护了多个能表示元素之间关系的数组如value等 带权并查集的基本操作1——merge //比如要维护有序队列中某个点离祖宗的距离 inline void merge(int x,int y) { int f1=find(x),f2=find(y); if(f1!=f2) { f[f1]=f2; value[f1]+=sum[f2]; sum[f2]+=sum[f1]; sum[f1]=0; } returnl } 带权并查集的基本操作二——查询 在合并后,被合并集合的信息并不是立马更改,而是在一次查询中全部更改 注意信息的修改需要在回溯的过程中实现 inline int find(int k) { if(f[k]==k) return f[k]; int fa=find(f[k]); value[k]+=value[f[k]]; return f[k]=fa;//路径压缩 } 来源: https://www.cnblogs.com/Hoyoak/p/11336666.html

并查集

烂漫一生 提交于 2019-11-27 00:32:07
并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题。常常在使用中以森林来表示。 一,对并查集的认识 并查集是树形结构,常常在题目中用来判断两个元素是否属于同一个集合,每个集合都有一个特征性元素称为这个集合的father,如果两个元素的father相同,则说明这两个元素属于同一集合,若这两个元素的father不相同,则说明这两个元素不属于一个集合。并查集就是这样一种支持合并和查询的树形数据结构。在题目中的应用有,判断两个点是否属于同一个联通块,最小生成树kruskal算法中,判断加边是否会有环等。 在考察并查集的题目中,一般我们用一个f数组来实现并查集的功能,起初所有的f[i]=i;证明所有元素都是单独一个集合。 //并查集初始化 for(int i=1;i<=n;i++) f[i]=i; 二,并查集的基本操作 1,并查集的合并操作(merge) 简而言之,就是将两个元素所在的集合合并成一个集合,操作简单,首先我们只需要找到两个集合的特征性元素,然后把其中一个特征性元素变成另一个,这样两个集合特征性元素相同,两个集合就合并在了一起,在merge操作中我们运用到了按秩合并来优化。 inline void merge(int f1,int f2) { f[f1]=f2; return; }//普通合并 按秩合并:所谓秩就是树高,按秩合并就是按照秩序合并

并查集c++实现

浪子不回头ぞ 提交于 2019-11-27 00:22:54
# include <iostream> # include <vector> # include <cassert> using namespace std ; class UnionFind { private : vector < int > parent ; int count ; //优化,记录p和q所在组的深度,在合并时将深度小的结点的根指向深度大的结点的根 vector < int > rank ; public : UnionFind ( int count ) { parent . resize ( count ) ; rank . resize ( count ) ; this - > count = count ; for ( int i = 0 ; i < count ; ++ i ) { parent [ i ] = i ; rank [ i ] = 1 ; } } ~ UnionFind ( ) { parent . clear ( ) ; rank . clear ( ) ; } //路径压缩 int find ( int p ) { assert ( p >= 0 && p < count ) ; if ( p != parent [ p ] ) parent [ p ] = find ( parent [ p ] ) ; return parent

并查集详解

不羁的心 提交于 2019-11-27 00:17:12
原文链接: https://blog.csdn.net/qq_41593380/article/details/81146850 超级有意思,好懂的并查集解释, 膜拜大神~~ 江湖上散落着各式各样的大侠,有上千个之多。他们没有什么正当职业,整天背着剑在外面走来走去,碰到和自己不是一路人的,就免不了要打一架。但大侠们有一个优点就是讲义气,绝对不打自己的朋友。而且他们信奉“朋友的朋友就是我的朋友”,只要是能通过朋友关系串联起来的,不管拐了多少个弯,都认为是自己人。这样一来,江湖上就形成了一个一个的帮派,通过两两之间的朋友关系串联起来。而不在同一个帮派的人,无论如何都无法通过朋友关系连起来,于是就可以放心往死了打。但是两个原本互不相识的人,如何判断是否属于一个朋友圈呢? 我们可以在每个朋友圈内推举出一个比较有名望的人,作为该圈子的代表人物。这样,每个圈子就可以这样命名“中国同胞队”美国同胞队”……两人只要互相对一下自己的队长是不是同一个人,就可以确定敌友关系了。 但是还有问题啊,大侠们只知道自己直接的朋友是谁,很多人压根就不认识队长。要判断自己的队长是谁,只能漫无目的的通过朋友的朋友关系问下去:“你是不是队长?你是不是队长?”这样,想打一架得先问个几十年,饿都饿死了,受不了。这样一来,队长面子上也挂不住了,不仅效率太低,还有可能陷入无限循环中。于是队长下令,重新组队

UVA11354 Bond 并查集

独自空忆成欢 提交于 2019-11-26 23:23:59
  题意:给出一张n个点m条边的无向图, 每条边有一个危险度,有q个询问, 每次给出两个点s、t,找一条路, 使得路径上的最大危险度最小 按秩合并并查集 不能路径压缩 否则就破坏了其树形结构 #include<bits/stdc++.h> using namespace std; //input by bxd #define rep(i,a,b) for(int i=(a);i<=(b);i++) #define repp(i,a,b) for(int i=(a);i>=(b);--i) #define ll long long #define see(x) (cerr<<(#x)<<'='<<(x)<<endl) #define pb push_back #define inf 0x3f3f3f3f #define CLR(A,v) memset(A,v,sizeof A) typedef pair<int,int>pii; ////////////////////////////////// const int N=1e6+10; int f[N],x,y,q,n,m,siz[N],w[N]; struct Edge { int u,v,w; }edge[N]; int find1(int x) { return f[x]==x?x:find1(f[x]); } void

算法笔记--可撤销并查集 && 可持久化并查集

血红的双手。 提交于 2019-11-26 23:05:22
可撤销并查集模板: struct UFS { stack<pair<int*, int>> stk; int fa[N], rnk[N]; inline void init(int n) { for (int i = 0; i <= n; ++i) fa[i] = i, rnk[i] = 0; } inline int Find(int x) { while(x^fa[x]) x = fa[x]; return x; } inline void Merge(int x, int y) { x = Find(x), y = Find(y); if(x == y) return ; if(rnk[x] <= rnk[y]) { stk.push({fa+x, fa[x]}); fa[x] = y; if(rnk[x] == rnk[y]) { stk.push({rnk+y, rnk[y]}); rnk[y]++; } } } inline void Undo() { *stk.top().fi = stk.top().se; stk.pop(); } }; 来源: https://www.cnblogs.com/widsom/p/11334747.html

最近目标2333

故事扮演 提交于 2019-11-26 21:03:18
最近目标 并查集: P2024 [NOI2001]食物链 P1525 关押罪犯 (二分+二分图)(并查集代表监狱)(并查集代表犯人之间的关系) 动态规划dp: P2016 战略游戏 P1352 没有上司的舞会   P1273 有线电视网 P3354 [IOI2005]Riv 河流 P3140 [USACO16FEB]再探圆形谷仓Circular Barn R… P3112 [USACO14DEC]后卫马克Guard Mark P3092 [USACO13NOV]没有找零No Change P2967 [USACO09DEC]视频游戏的麻烦Video Game Troubles P3116 [USACO15JAN]约会时间Meeting Time P3509 [POI2010]ZAB-Frog 树上dp: 洞窟探索 P2015 二叉苹果树 P2014 选课 二分: T8662 吃桃子 树链剖分 : P3178 [HAOI2015]树上操作 P3384 【模板】树链剖分 P1600 天天爱跑步    二叉排序树: P1908 逆序对 图论: P3385 【模板】负环 贪心/暴力: P3745 [六省联考2017]期末考试 P3621 [APIO2007]风铃 哈希,hash: P3538 [POI2012]OKR-A Horrible Poem 我也不知道是什么233(我太弱了:

并查集初步

谁说我不能喝 提交于 2019-11-26 19:43:39
并查集可以动态维护若干个不重叠的集合,支持查询和合并两个操作,在实际应用中比较广泛。 并查集的主要功能是查询元素的集合归属,同时支持集合的合并操作。 并查集的实现方法:对于每个集合,选择一个元素作为其代表元素,而若两个元素所在集合的代表元素相同,则说明它们在同一个集合中。 具体如何实现呢?有一种思路,可以定义一个数组来存储每个数所在集合的代表元素,然而这种方法虽然在查询时很高效,但在合并时却要耗费大量的时间。 因此我们可以有另一种方法:将整个并查集看作一个森林,定义一个数组 fa[i] 存储 i 的父节点,初始化 fa[i]=i ,则查询一个元素的代表元素时,可以沿着其父亲节点不断向上找到根节点,从而实现查询。而合并时,合并两个集合的根即可,即令 fa[root1]=roo2 。 在以上的基础上,我们还可以进行进一步地优化。我们发现,第一种思路查询时非常高效,因此我们可以将两种思路进行结合,在递归查询代表元素时,将路径上每个元素 x x 的 fa f a 值直接指向树根,可以节省很多的时间。这个优化方法被称为“路径压缩”。 还有一种被称为“按秩合并”的优化方法,读者可以自行了解。不过一般的实现中用路径压缩就足够了。 int get(int a) { if(fa[a]==a) return a; return fa[a]=get(fa[a]); } void merge(int x