
💡Yupureki:个人主页
✨个人专栏:《C++》 《算法》《Linux系统编程》《高并发内存池》
🌸Yupureki🌸的简介:

目录
[1. 并查集前言](#1. 并查集前言)
[1.1 并查集的初始化](#1.1 并查集的初始化)
[1.2 查询操作](#1.2 查询操作)
[1.3 合并操作](#1.3 合并操作)
[1.4 判断操作](#1.4 判断操作)
[2. 【模板】并查集](#2. 【模板】并查集)
[3. 亲戚](#3. 亲戚)
[4. [USACO10OCT] Lake Counting S](#4. [USACO10OCT] Lake Counting S)
[5. 程序自动分析](#5. 程序自动分析)
1. 并查集前言
在有些问题中,我们需要维护若干个集合,并且基于这些集合要频繁执行下面的操作:
- 查询操作:查找元素属于哪一个集合。一般会在每个集合中选取一个元素作为代表,查询的是这个集合中的代表元素;
- 合并操作:将元素所在的集合与元素y所在的集合合并成一个集合;(注意,合并的是元素所在的集合,不是这两个元素!)
- ·判断操作:判断元素和y是否在同一个集合。
因此这就引出了一个新的数据结构:并查集。并查集的本质是一课树,这颗树的上的所有节点具有相同的某种属性,因此串在一起,表示一个集合。

1.1 并查集的初始化
我们通常用数组来表示并查集,这个数组中的下标表示节点序号 ,值表示他的父节点
初始时,其父节点就是自己
cpp
const int N = 1e6 + 10;
int n;
int fa[N]; // 双亲表⽰法所需的数组
// 初始化并查集
void init()
{
for(int i = 1; i <= n; i++) fa[i] = i;
}
1.2 查询操作
并查集中一颗树中有很多个节点,为了方便表示一棵树,我们通常用一颗树的根节点来代表这课树。因此如果两个节点他们的根节点相同 ,代表他们在同一个并查集中
根节点的父节点等于其自身,因此遇到父节点等于其自身时即可停下
cpp
// 查询操作
int find(int x)
{
return fa[x] == x ? x : find(fa[x]);
}

例如在上图中,从H和G不停地向上找,直到找到根节点,此时发现他们的根节点相同,因此他们在同一个并查集中
1.3 合并操作
将x元素所在的集合与元素y所在的集合合并成一个集合:
本质其实就是让y的根节点成为x的根节点的父节点,这样顺带x和y都被连在了一起

cpp
// 合并操作
void un(int x, int y) // 注意,函数名字不能⽤ union,因为它是 C++ 的关键字
{
int fx = find(x);
int fy = find(y);
fa[fx] = fy;
}
1.4 判断操作
判断元素c和元素y是否在同一集合:
看看两者所在树的根节点是否相同。
cpp
bool issame(int x, int y)
{
return find(x) == find(y);
}
2. 【模板】并查集
题目链接:

算法原理
并查集的模板题
代码实现
cpp
#include <iostream>
#include <vector>
using namespace std;
vector<int> v(1);
int find(int x)
{
return v[x] == x?x:v[x] = find(v[x]);
}
void un(int x,int y)
{
int rootx = find(x);
int rooty = find(y);
v[rootx] = rooty;
}
int main()
{
int n,m;
cin>>n>>m;
for(int i = 1;i<=n;i++)
v.push_back(i);
while(m--)
{
int a,b,c;cin>>a>>b>>c;
if(a == 1)
un(b,c);
else
{
if(find(b) == find(c))
cout<<"Y"<<endl;
else
cout<<"N"<<endl;
}
}
return 0;
}
3. 亲戚
题目链接:

算法原理
你与亲戚的亲戚之间也是亲戚
因此把所有具有亲戚相关的节点看作一个大家庭,相当于在并查集中串在一起
代码实现
cpp
#include <iostream>
#include <vector>
using namespace std;
vector<int> father(1);
int find(int x)
{
return father[x] == x ? x : father[x] = find(father[x]);
}
void un(int x, int y)
{
int fx = find(x);
int fy = find(y);
father[fx] = fy;
}
int main()
{
int n, m, p; cin >> n >> m >> p;
for (int i = 1; i <= n; i++)
{
father.push_back(i);
}
for (int i = 0; i < m; i++)
{
int a, b; cin >> a >> b;
un(a, b);
}
while (p--)
{
int a, b; cin >> a >> b;
if (find(a) == find(b))
cout << "Yes" << endl;
else
cout << "No" << endl;
}
return 0;
}
4. [USACO10OCT] Lake Counting S
题目链接:

算法原理
bfs + 并查集
在bfs中,把相邻的水地用并查集串在一起,表示为一整个大池塘
代码实现
cpp
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
int n, m;
vector<vector<char>> v;
vector<int> father;
int dx[4] = { 1,1,1,0 };
int dy[4] = { -1,1,0,1 };
int find(int x)
{
return father[x] == x ? x : father[x] = find(father[x]);
}
void un(int x, int y)
{
int fx = find(x);
int fy = find(y);
father[fx] = fy;
}
void bfs(int x, int y)
{
queue<pair<int, int>> q;
q.push({ x,y });
int pos = x * n + y;
father[pos] = pos;
while (q.size())
{
auto p = q.front();
q.pop();
int a = p.first;
int b = p.second;
for (int k = 0; k < 4; k++)
{
int i = a + dx[k];
int j = b + dy[k];
if (i < 0 || i >= n || j < 0 || j >= m || v[i][j] == '.')
continue;
un(i*n+j,a*n+b);
q.push({ i,j });
}
}
}
int main()
{
cin >> n >> m;
v.resize(n);
father.assign(n * m, -1);
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
char ch; cin >> ch;
v[i].push_back(ch);
}
}
int ret = 0;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
if (v[i][j] == 'W' && father[i * n + j] == -1)
{
ret++;
bfs(i, j);
}
}
}
cout << ret;
return 0;
}
5. 程序自动分析
题目链接:

算法原理
先利用并查集维护所有相等的信息,然后遍历所有的不相等信息,判断一下是否合法。
因为数据范围的问题,需要先对所有的数离散化处理。
代码实现
cpp
#include <iostream>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <algorithm>
using namespace std;
vector<int> fa;
int find(int x)
{
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
void un(int x, int y)
{
int fx = find(x);
int fy = find(y);
fa[fx] = fy;
}
int main()
{
int n; cin >> n;
while (n--)
{
fa.clear();
vector<long long> v;
unordered_map<long long, int> dis;
vector<pair<long long, long long>> m;
vector<pair<long long, long long>> check;
int k; cin >> k;
while (k--)
{
long long i, j, e; cin >> i >> j >> e;
v.push_back(i);
v.push_back(j);
if (e == 1)
m.push_back({ i,j });
else
check.push_back({ i,j });
}
sort(v.begin(), v.end());
v.erase(unique(v.begin(), v.end()),v.end());
for (int i = 0; i < v.size(); i++)
dis.insert({ v[i],i });
for (int i = 0; i < dis.size();i++)
fa.push_back(i);
for (auto& it : m)
{
un(dis[it.first], dis[it.second]);
}
int r = 1;
for (auto& it : check)
{
if (find(dis[it.first]) == find(dis[it.second]))
{
cout << "NO" << endl;
r = 0;
break;
}
}
if (r)
cout << "YES" << endl;
}
return 0;
}