并查集總結

并查集簡介

并查集是一種可以動態維護若干個不重疊的結合,并支持合并與查詢的數據結構
并查集是一種樹狀的數據結構,可以用于維護傳遞關系以及聯通性。
并查集有兩種操作:

  1. find:查詢一個元素屬于哪個集合
  2. merge:合并兩個集合

模板

  1. find函數
int find(int x)
{if(x == fa[x])	return x;reutnr fa[x] = find(fa[x]);
}
  1. merge函數
void merge(int x, int y)
{int px = find(x), py = find(y);if(px != py)	fa[px] = py;
}

模板題

村村通
對于這道題我們只需要求連通塊的數量,然后將這幾個聯通快看成點,聯通n個點需要n-1條邊

#include <iostream>
#include <cstring>using namespace std;
const int N = 1010;
int p[N], st[N];int find(int x)
{if(x == p[x])   return x;return p[x] = find(p[x]);
}int main()
{// freopen("1.in", "r", stdin);while(1){memset(st, 0, sizeof st);int n, ans = 0;scanf("%d", &n);if(n == 0)  return 0;else{int m;  scanf("%d", &m);for(int i = 1; i <= n; ++ i)    p[i] = i;for(int i = 0; i < m; ++ i){int x, y;   scanf("%d%d", &x, &y);int a = find(x);int b = find(y);if(a != b)  p[a] = b;}//統計聯通塊的數量for(int i = 1; i <= n; ++ i){int x = find(i);if(!st[x])  ans ++, st[x] = 1;;}cout << ans - 1 << endl;}}
}

P1551 親戚
親戚的親戚是親戚,親戚這種關系具有傳遞性,如果具有親戚關系就將合并。

#include <iostream>using namespace std;const int N = 5010;int p[N];
int n, m, t;int find(int x)
{if(x != p[x])   p[x] = find(p[x]);return p[x];
}int main()
{cin >> n >> m >> t;for(int i = 1; i <= n; ++ i)    p[i] = i;for(int i = 0; i < m; ++ i){int a, b;cin >> a >> b;int fa = find(a);int fb = find(b);p[fa] = fb;}for(int i = 0; i < t; ++ i){int a, b;cin >> a>> b;int fa = find(a);int fb = find(b);if(fa != fb)    puts("No");else    puts("Yes");}return 0;
}

836. 合并集合

#include <iostream>using namespace std;const int N = 1e5+10;int n,m;
int p[N];int find(int x)
{if(p[x] != x )  p[x] = find(p[x]);return p[x];
}int main(int argc, const char * argv[]) {scanf("%d%d",&n,&m);for(int i = 0; i < n; i ++) p[i] = i;while(m--){char op[2];int a,b;scanf("%s%d%d",op,&a,&b);if(op[0] == 'M')    p[find(a)] = find(b);else{if(find(a) == find(b))  cout<<"Yes"<<endl;else puts("No");}}return 0;
}

837. 連通塊中點的數量
這道題目比前面的幾道模板題目需要多維護一個信息,在進行merge時我們需要將更新作為被添加枝的樹的cnt
兩個bug調了一個小時最后看了下評論區收獲頗豐
1、合并兩個集合時
如果沒有按照下面的寫法即省去這一步a=find(a),b=find(b);
則合并根節點的順序與更新更新集合得順尋不能互換,
必須要先把原來根節點中元素的數量加到所要合并的
根節點上去再把根節點合并
a=find(a),b=find(b);
cnt[b]+=cnt[a];
p[a]=b;
2、路徑壓縮
一定不要忘記路徑壓縮不然會超時!!!

#include"bits/stdc++.h"
using namespace std;
const int N=1e5+10;
int p[N],cnt[N];
int n,m;
int find(int x)
{if(p[x] != x)  p[x] = find(p[x]);return p[x];
}
int main()
{cin.tie(0);cin>>n>>m;for(int i=1;i<=n;i++)   {p[i]=i;cnt[i]=1;}while(m--){string str;int a,b;cin>>str;if(str=="C"){cin>>a>>b;if(find(a)!=find(b))   {a=find(a),b=find(b);cnt[b]+=cnt[a];p[a]=b;}}else if(str=="Q1"){cin>>a>>b;if(find(a)==find(b))    cout<<"Yes"<<endl;else                    cout<<"No"<<endl;}else{cin>>a;cout<<cnt[find(a)]<<endl;}}return 0;
}

邊帶權并查集

推導部分和
這道題的是帶邊權并查集的應用,比較難想到的是建圖
對于每個區間l, r,k, 我們可以由前綴和s[r] - s[l - 1] = k,我們從r連一條l-1的邊
WechatIMG819.png

#include <iostream>using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
//p[N]數組用來做并查集
int p[N], n, m, q;
//s[N]數組是當前點到根結點的權值和,也是前綴和
LL s[N];//并查集的查詢操作(帶路徑壓縮)
int find(int x)
{if(x == p[x])   return x;else{int t = find(p[x]);s[x] += s[p[x]];p[x] = t;return t;}
}int main()
{// freopen("1.in", "r", stdin);cin >> n >> m >> q;for(int i = 1; i <= n; ++ i)    p[i] = i;for(int i = 0; i < m; ++ i){int l ,r;LL k; cin >> l >> r >> k;int t1 = find(l - 1), t2 = find(r);if(t1 != t2){p[t2] = t1;s[t2] = s[l - 1] - s[r] + k;}}while(q --){int l, r;cin >> l >> r;int t1 = find(l - 1), t2 = find(r);if(t1 != t2)    puts("UNKNOWN");else printf("%lld\n", s[r] - s[l - 1]);}return 0;
}

P1196 [NOI2002] 銀河英雄傳說
這道題目比較特殊是每個集合是一條鏈,一條鏈也是一棵樹,不過是樹的特殊形態,我們可以把每一列看作一個集合,用并查集去維護,另外題目中需要知道兩個點之間的點有多少個,這里我們就還需要額外維護每個點到根節點路徑上的權值,因為我們這里的并查集已經進行優化即使用了路徑壓縮,并且邊權都是1,所以在維護每個點到根節點的路徑上的權值時,我們還需要用到一個集合中元素的個數,也就是還需要額外維護集合中元素個數。
綜上我們需要額外維護兩個信息:

  1. d[x]:表示x到根節點的邊權求和
  2. size[x]:表示以x為根的子樹中節點數量
#include <bits/stdc++.h> 
#define ls p<<1
#define rs p<<1|1
#define PII pair<int, int>
#define ll long long
#define ull unsigned long long
#define endl '\n'
#define db double
using namespace std;const int N = 30010;
int fa[N], d[N], size[N],t;void init()
{for(int i = 1; i <= N - 1; ++ i)	fa[i] = i, size[i] = 1;
}int find(int x)
{if(x == fa[x])	return x;int root = find(fa[x]);d[x] += d[fa[x]];return fa[x] = root;
}void merge(int x, int y)
{int px = find(x), py = find(y);if(px == py)	return;fa[px] = py;d[px] = size[py];size[py] += size[px];
}void solve()
{cin >> t;init();for(int i = 1; i <= t; ++ i){char op;int x, y;cin >> op >> x >> y;if(op == 'M')	merge(x, y);else{int px = find(x), py = find(y);if(px != py)	cout << -1 << endl;else	cout << abs(d[x] - d[y]) - 1 << endl;		} }
}int main()
{ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
//	freopen("1.in", "r", stdin);
//	cin >> t;
//	while(t --)	solve(); return 0;
}

并查集的拓展域

P2024 [NOI2001] 食物鏈
這是一道并查集的拓展域的題目也可以用帶權并查集去做
普通并查集維護的是不相交集合,是一種傳遞性的關系如親戚的親戚是親戚
天敵的天敵是食物,是一種環形的關系
我們如果用拓展域來解決這道題目的話可以用3個并查集來維護3種關系,第一種是同類關系,第二種是食物關系,第三種是天敵
我們不用真開三個并查集,我們可以將三個并查集開到一個數組里,下標的范圍代表不同的集合
WechatIMG824.jpeg

#include <iostream>using namespace std;
const int N = 5e4 + 10;
int p[N * 3], ans, k, n;//1--n代表x的同類,n + 1 -- 2n代表x的食物, 2n + 1 -- 3n代表x的天敵
int find(int x)
{if(x == p[x])   return x;return p[x] = find(p[x]);
}
void merge(int x, int y)
{int px = find(x), py = find(y);p[py] = px;
}
int main()
{cin >> n >> k;for(int i = 1; i <= 3 * n; ++ i)    p[i] = i;for(int i = 0; i < k; ++ i){int d, x, y;scanf("%d%d%d", &d, &x, &y);if(x > n || y > n)  ans ++;//x、y是同類else if(d == 1){//如果根據前面的信息,我們可以知道y在x的食物域,//或者y在x的天敵域中,說明這句話是假話if(find(x + n) == find(y) || find(x + n + n) == find(y))    ans ++;//如果根據前面的信息,不能判斷這句話是錯誤的,那么就講這句話//當成對的并且更新x的三個域else{//y在x的同類域中merge(x, y);//y的食物和x的食物是同類merge(x + n, y + n);//y的天敵和x的天敵是同類merge(x + n + n, y + n + n);}}//如果x吃yelse{//若果y在x的同類域或者,y在x的天敵域說明這句話是假話if(find(x) == find(y) || find(x + n + n) == find(y))    ans ++;//這句話是真話就更新x的三個域else{//y在x的食物域中merge(x + n, y);//y的食物是x的天敵merge(x + n + n, y + n);//y的天敵是x的同類merge(x, y + n + n);}}}cout << ans << endl;return 0;
}

例題團伙

#include <iostream>
using namespace std;
const int N = 2010;
int p[N];
bool st[N];
int find(int x)
{if(p[x] == x)   return p[x];return p[x] = find(p[x]);
}
void merge(int x, int y)
{int px = find(x), py = find(y);if(px != py)    p[px] = py;
}int main()
{// freopen("1.in", "r", stdin);int n, m;scanf("%d%d", &n, &m);for(int i = 1; i <= 2 * n; ++ i)    p[i] = i;for(int i = 0; i < m; ++ i){char op; int x, y;cin >> op >> x >> y;if(op == 'F'){merge(x, y);// merge(x + n, y + n);}else{merge(x + n, y);merge(x, y + n);}}// for(int i = 1; i <= n; ++ i)    cout << i << ' ' << find(i) << endl;    int cnt = 0;for(int i = 1; i <= n; ++ i)if(!st[find(i)]){cnt ++;st[find(i)] = 1;}cout << cnt << endl;return 0;
}

P5937 [CEOI1999] Parity Game

題目的具體思路如下:考慮一段連續區間的1個數的奇偶,可以轉化為考慮考慮兩個端點前綴和的奇偶性上,分為兩種情況,如果[l, r]區間內1的個數為偶數,說明sum[r]和sum[l - 1]包含1的個數的奇偶性相同,反之若為奇數則兩個包含1的個數的奇偶性相反,我們知道奇偶性具有傳遞性,這樣我們就可以種類并查集來維護,注意n的范圍比較大,但是實際的需要用到的點的個數是比較小的,這里我們需要離散化一下。

#include <bits/stdc++.h> 
#define LL long long 
using namespace std;const int N = 1e4 + 10;
int p[N * 2 + 10], n, m, k;
map<int, int>mp;
int b[N * 2];struct wy
{int l, r, op;	
}q[N];int find(int x)
{if(x != p[x])	p[x] = find(p[x]);return p[x];
}void merge(int x, int y)
{int px = find(x), py = find(y);p[py] = px;
}int query(int x)
{return lower_bound(b + 1, b + 1 + k, x) - b;
}
void solve()
{scanf("%d%d", &n, &m);for(int i = 1; i <= m; ++ i){int l, r, op;char s[5];scanf("%d%d%s", &l, &r, s);if(s[0] == 'e')	op = 1;else op	= 2;q[i] = {--l, r, op};b[++ k] = l;b[++ k] = r;} sort(b + 1, b + 1 + k);k = unique(b + 1, b + 1 + k) - (b + 1);for(int i = 1; i <= 2 * k; ++ i) p[i] = i;for(int i = 1; i <= m; ++ i){int l = query(q[i].l), r =  query(q[i].r), op = q[i].op;if(op == 1) {if(find(l) == find(r + k) || find(r) == find(l + k)){printf("%d", i - 1);return;}else{merge(l, r);merge(l + k, r + k);}}else{if(find(l) == find(r) || find(r + k) == find(l + k)){printf("%d", i - 1);return;}else{merge(l, r + k);merge(r, l + k); 	}}	}printf("%d", m);
}
int main()
{
//	freopen("1.in", "r", stdin);solve(); return 0;
}

應用

P1955 [NOI2015] 程序自動分析

這道題目是相等關系,相等關系也具有傳遞性,明顯我們可以用并查集來維護。
我們可以先對處理相等,然后去查詢不相等的是否在一個集合里面如果在一個集合里面則說明這樣的點是不存在的。這道題目的數據的范圍很大,但實際用到的很少,我們需要對數據進行離散化。

#include <bits/stdc++.h> 
#define LL long long 
using namespace std;const int N = 1e6 + 10;
int n, m, p[N], a[N], k, tot;
struct wy{int x, y, e;
}q[N];int find(int x)
{if(x != p[x])	p[x] = find(p[x]);return p[x];
} void merge(int x, int y)
{int px = find(x), py = find(y);p[py] = px;
}void solve()
{scanf("%d", &n);for(int i = 1; i <= n; ++ i){int x, y, e;scanf("%d%d%d", &x, &y, &e);a[++ tot] = q[i].x = x;a[++ tot] = q[i].y = y;q[i].e = e;	}sort(a + 1, a + 1 + tot);tot = unique(a + 1, a + 1 + tot) - a - 1;for(int i = 1; i <= tot; ++ i)	p[i] = i;for(int i = 1; i <= n; ++ i){q[i].x = lower_bound(a + 1, a + tot + 1, q[i].x) - a - 1;q[i].y = lower_bound(a + 1, a + tot + 1, q[i].y) - a - 1;if(q[i].e == 1){merge(q[i].x, q[i].y);}}for(int i = 1; i <= n; ++ i){int x = q[i].x;int y = q[i].y;if(q[i].e == 0 && find(x) == find(y)){puts("NO");return;}}puts("YES");
}int main()
{
// 	freopen("1.in", "r", stdin);scanf("%d", &k);while(k--) solve(); return 0;
}

P1525 [NOIP2010 提高組] 關押罪犯
貪心+并查集,我們優先選擇邊權最大的罪犯,首先查詢他們是否已經在一個集合 如果不在,分別將他們放進不同監獄(集合),如果在則說明我們已經找到了答案

#include <bits/stdc++.h> 
#define ls p<<1
#define rs p<<1|1
#define PII pair<int, int>
#define ll long long
#define ull unsigned long long
#define endl '\n'
#define db double
using namespace std;const int N = 1e6 + 10, e = 20010;struct wy
{int x, y, val;bool operator < (const wy & t) const{return t.val < val;}
}a[N];int p[N + 1];
int n, m;void init()
{for(int i = 1; i <= N; ++ i)	p[i] = i;
}int find(int x)
{if(x == p[x])	return x;return p[x] = find(p[x]);	
}void merge(int x, int y)
{int px = find(x), py = find(y);if(px != py)	p[py] = px;
}void solve()
{cin >> n >> m;for(int i = 1; i <= m; ++ i){int x, y, val; cin >> x >> y >> val;a[i] = {x, y, val};}sort(a + 1, a + 1 + m);init();for(int i = 1; i <= m; ++ i){int x = a[i].x, y = a[i].y, val = a[i].val;int px = find(x), py = find(y);if(px == py)	{cout << val << endl;return;}else{merge(y + n, x);merge(x + n, y);}}cout << 0 << endl;
}
int main()
{ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
//	freopen("1.in", "r", stdin);
//	cin >> t;
//	while(t --)	solve(); return 0;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/160118.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/160118.shtml
英文地址,請注明出處:http://en.pswp.cn/news/160118.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

爆款文章有訣竅,內容創作者如何能持續產出優質內容

內容營銷人有沒有這么一種共鳴&#xff1a;10 萬 那么多&#xff0c;為什么不能多我一個&#xff1f; 通常&#xff0c;我們把瀏覽量 / 閱讀量高、轉評贊數量高的內容看作爆款&#xff0c;而數據如果達到 10 萬 則是超級爆款。因為&#xff0c;閱讀量高意味著內容得到了大量的曝…

【Linux】使用Makefile自動化編譯項目:簡化開發流程、提高效率

文章目錄 示例一&#xff1a;編譯一個進度條程序示例二&#xff1a;編譯一個簡單的程序gcc的幾個選項結論 當你開始一個新的軟件項目時&#xff0c;編寫一個好的Makefile是非常重要的。Makefile是一個文本文件&#xff0c;用于指定如何構建和編譯項目。它定義了目標文件、依賴關…

8年老鳥整理,自動化測試-準備測試數據詳細...

目錄&#xff1a;導讀 前言一、Python編程入門到精通二、接口自動化項目實戰三、Web自動化項目實戰四、App自動化項目實戰五、一線大廠簡歷六、測試開發DevOps體系七、常用自動化測試工具八、JMeter性能測試九、總結&#xff08;尾部小驚喜&#xff09; 前言 大部分類型的測試…

基于C#實現Bitmap算法

在所有具有性能優化的數據結構中&#xff0c;我想大家使用最多的就是 hash 表&#xff0c;是的&#xff0c;在具有定位查找上具有 O(1)的常量時間&#xff0c;多么的簡潔優美&#xff0c;但是在特定的場合下&#xff1a; ①&#xff1a;對 10 億個不重復的整數進行排序。 ②&am…

python獲取透明圖

import cv2 import os import numpy as nproot "./test" for file in os.listdir(root):# 讀取圖片image cv2.imread(os.path.join(root, file), cv2.IMREAD_UNCHANGED)new np.zeros((image.shape[0], image.shape[1], image.shape[2]), np.uint8)# 檢查圖片是否為…

AI原生應用為百度帶來新增量

我是盧松松&#xff0c;點點上面的頭像&#xff0c;歡迎關注我哦&#xff01; AI將徹底改變每一個行業!得益于AI和基礎模型的驅動&#xff0c;百度在AI原生應用領域厚積薄發。 11月21日&#xff0c;百度Q3財報發布&#xff0c;數據顯示&#xff1a;三季度營收達344.47億元&…

Redis篇---第九篇

系列文章目錄 文章目錄 系列文章目錄前言一、如果有大量的 key 需要設置同一時間過期,一般需要注意什么?二、什么情況下可能會導致 Redis 阻塞?三、緩存和數據庫誰先更新呢?前言 前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊…

Axios簡單使用與配置安裝-Vue

安裝Axios npm i axios main.js 導入 import Axios from axios Vue.prototype.$axios Axios簡單發送請求 get getTest() {this.$axios({method: GET,url: https://apis.jxcxin.cn/api/title?urlhttps://apis.jxcxin.cn/}).then(res > {//請求成功回調console.log(res)}…

uiautomator2快速入門app自動化測試教程

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 前言一、環境準備1.安裝軟件2.安裝庫 二、adb 連接手機1. 準備工作2. 第一種連接方式&#xff1a;USB連接3. 第二種連接方式&#xff1a;WLAN連接4. 第三種連接方式…

②⑩ 【MySQL Log】詳解MySQL日志:錯誤日志、二進制日志、查詢日志、慢查詢日志

個人簡介&#xff1a;Java領域新星創作者&#xff1b;阿里云技術博主、星級博主、專家博主&#xff1b;正在Java學習的路上摸爬滾打&#xff0c;記錄學習的過程~ 個人主頁&#xff1a;.29.的博客 學習社區&#xff1a;進去逛一逛~ MySQL日志 ②⑩ MySQL日志&#xff1a;錯誤日志…

SpringBoot3.x最簡集成SpringDoc-OpenApi

為什么使用SpringDoc 在SpringBoot低版本時一般使用Swagger掃描接口生成Json格式的在線文檔&#xff0c;然后通過swagger-ui將Json格式的文檔以頁面形式展示文檔。可惜遺憾的是swagger更新到3.0.0版本(springfox)后不更新了。 SpringBoot3.x以后需要的JDK版本最低為Java17&…

MQ和redis的內部原理一些總結

首先&#xff0c;先知道內部原理&#xff1b;其次&#xff0c;就是查官方文檔實戰了。 但是如果不熟悉內部原理&#xff0c;那么僅僅只是安裝官方文檔&#xff0c;并不能排除跟蹤問題和故障、預防風險等策略&#xff1b; 以下總結圖解&#xff1a;&#xff08;mysql 8.0新增的…

YOLO目標檢測——衛星遙感艦船檢測數據集下載分享【含對應voc、coco和yolo三種格式標簽】

實際項目應用&#xff1a;衛星遙感艦船檢測數據集說明&#xff1a;衛星遙感艦船檢測數據集&#xff0c;真實場景的高質量圖片數據&#xff0c;數據場景豐富&#xff0c;含船一個類別標簽說明&#xff1a;使用lableimg標注軟件標注&#xff0c;標注框質量高&#xff0c;含voc(xm…

Redis的持久化

redis是一個內存數據庫,是把數據存儲在內存中的,而我們知道內存中的數據是不持久的,一旦服務器重啟或者進程重啟,內存的數據就丟失了.為了讓數據達到持久化的效果,就必須把數據寫到硬盤上. redis相對于mysql這樣的關系型數據庫最明顯的優勢就是快.所以為了保證速度快,數據還得…

動態跳過測試用例

動態跳過測試用例 說明 我們可以通過指定環境變量來動態判斷是否執行指定的測試用例設置環境變量有很多種方法&#xff0c;例如命令行方式&#xff0c;格式&#xff1a;--env keyval1,key2val2 &#xff0c;若需要指定多個環境變量則需要逗號來隔開&#xff0c;而不是空格 t…

Live800:企業提升客戶互動體驗,有哪些關鍵因素?

如今&#xff0c;隨著信息時代的不斷發展&#xff0c;企業已經不再是單向的商業機構&#xff0c;他們需要與客戶進行及時的溝通與反饋&#xff0c;從而更好地提升客戶互動體驗&#xff0c;達到營銷和用戶體驗的雙贏局面。那么&#xff0c;企業如何提升客戶互動體驗呢&#xff1…

設計模式——RBAC 模型詳解

1.什么是 RBAC 呢&#xff1f; RBAC 即基于角色的權限訪問控制&#xff08;Role-Based Access Control&#xff09;。這是一種通過角色關聯權限&#xff0c;角色同時又關聯用戶的授權方式。 簡單地說&#xff1a;一個用戶可以擁有若干角色&#xff0c;每一個角色又可以被分配…

Mysql 中如何導出數據?

文章目錄 前言MySQL 導出數據使用 SELECT ... INTO OUTFILE 語句導出數據SELECT ... INTO OUTFILE 語句有以下屬性:導出表作為原始數據導出SQL格式的數據將數據表及數據庫拷貝至其他主機 后言 前言 hello world歡迎來到前端的新世界 &#x1f61c;當前文章系列專欄&#xff1a;…

Linux程序之可變參數選項那些事!

一、linux應用程序如何接收參數&#xff1f; 1. argc、argv Linux應用程序執行時&#xff0c;我們往往通過命令行帶入參數給程序&#xff0c;比如 ls /dev/ -l 其中參數 /dev/ 、-l都是作為參數傳遞給命令 ls 應用程序又是如何接收這些參數的&#xff1f; 通常應用程序都…