关于并查集的一些套路和用途

并查集的模板(加上了路径压缩)

int find(int x)
{
	if (x != p[x]) p[x] = find(p[x]);
	return p[x];
}

1.并查集常被用于判断一张图是否连通,或是连通的最小代价(也就是最小生成树问题)或是最小步数

例如:
P1111 修复公路
P1546 [USACO3.1]最短网络 Agri-Net

#include <iostream>
#include <algorithm>
#include <string>
#include <map>

using namespace std;

typedef pair<int, int> PII;
const int N = 200010, INF = 1e9;
int p[N], sz[N];

struct S{
	int x, y, z;
}s[N];

int find(int x)
{
	if (x != p[x]) p[x] = find(p[x]);
	return p[x];
}

bool cmp(S a, S b)
{
	return a.z < b.z;
}

void solve()
{
	int n;
	cin >> n;
	int ans = 0;
	int idx = 0;
	for (int i = 1; i <= 110; i ++) p[i] = i;
	
	for (int i = 1; i <= n; i ++)
	{
		for (int j = 1; j <= n; j ++)
		{
			cin >> s[++ idx].z;
			s[idx].y = j;
			s[idx].x = i;
		}
	}
	
	sort(s + 1, s + 1 + idx, cmp);
	
	for (int i = 1; i <= idx; i ++)
	{
		int px = find(s[i].x), py = find(s[i].y);
		if (px != py)
		{
			p[px] = py;
			ans += s[i].z;
		}
	}
	
	cout << ans << endl;
}

int main()
{
	int t = 1;
	while (t --)
	{
		solve();
	}
	return 0;
}

2.判断去掉某边后,是否还能构成连通图

例如:
P1656 炸铁路
P6121 [USACO16OPEN]Closing the Farm G

#include <iostream>
#include <algorithm>
#include <string>
#include <map>
#include <cstring>
#include <queue>

using namespace std;

typedef pair<int, int> PII;
const int N = 200010, INF = 1e9, M = N / 2;
int p[N], sz[N];
int idx, ans[N], tmp[N], h[N];
bool st[N];

struct Edge{
	int from;
	int to;
	int next;
}e[2 * N];

int find(int x)
{
	return x == p[x] ? p[x] : p[x] = find(p[x]);
}

void add(int x, int y)
{
	e[++ idx].from = x;
	e[idx].to = y;
	e[idx].next = h[x];
	h[x] = idx;
}

void solve()
{
	int n, m;
	cin >> n >> m;
	memset(h, -1, sizeof h);
	
	for (int i = 1; i <= n; i ++) p[i] = i;
	
	for (int i = 1; i <= m; i ++)
	{
		int u, v;
		cin >> u >> v;
		add(u, v);
		add(v, u);
	}
	
	for (int i = 1; i <= n; i ++)
	{
		cin >> tmp[i];
	}
	
	ans[n] = 1;
	st[tmp[n]] = 1;
	
	int k = 0;
	
	for (int i = n - 1; i >= 1; i --)
	{
		st[tmp[i]] = 1;
		for (int j = h[tmp[i]]; j; j = e[j].next)
		{
			if (st[e[j].to] == 1)
			{
				int px = find(tmp[i]), py = find(e[j].to); 
				if (px != py)
				{
					k ++;
					p[px] = py;
				}
			}
		}
		if (k == n - i) ans[i] = 1;
		else ans[i] = 0;
	}
	
	for (int i = 1; i <= n; i ++) 
		if (ans[i]) cout << "YES" << endl;
	else cout << "NO" << endl;
}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t = 1;
	while (t --)
	{
		solve();
	}
	return 0;
}

3.判断闭环内元素个数

例如:
P2661 [NOIP2015 提高组] 信息传递

#include <iostream>
#include <algorithm>
#include <string>
#include <map>
#include <cstring>
#include <queue>
#include <cmath>
#include <stack>
#include <iomanip>

using namespace std;

typedef pair<int, int> PII;
const int N = 200010, INF = 1e9, M = N / 2;
int p[N], d[N];

int find(int x)
{
	if (x != p[x])
	{
		int root = p[x];
		p[x] = find(p[x]);
		d[x] += d[root];
	}
	
	return p[x];
}

void solve()
{
	int n;
	cin >> n;
	
	for (int i = 1; i <= n; i ++) p[i] = i;
	
	int ans = INF;
	
	for (int i = 1; i <= n; i ++)
	{
		int x;
		cin >> x;
		int px = find(i), py = find(x);
		
		if (px != py)
		{
			p[px] = py;
			d[i] = d[x] + 1;
		}
		else {
			ans = min(d[x] + d[i] + 1, ans);
		}
	}
	
	cout << ans << endl;
}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t = 1;
	while (t --)
	{
		solve();
	}
	return 0;
}

4.带权并查集

P6691 选择题

posted @ 2022-10-13 21:30  nobodyL  阅读(40)  评论(0)    收藏  举报