Codeforces Round #541 (Div. 2) F. Asya And Kittens 并查集 | 启发式合并

题解

题目大意,1到n编号的数字最开始每个都属于一个单独的集合,告诉你相邻的两个集合并且合并在一起,最终会合并为同一个集合,输出任意满足条件的原序列。

并查集解法:
使用两个并查集vl, vr分别记录当前集合的最左元素和最右元素,在合并时将记录最左元素的根节点接在左侧集合的根节点下,记录右侧的接在右侧根节点下即vr[ar] = br, vl[bl] = al,ar, br为两个集合的根节点。
在拼接过程中再记录拼接点的左右元素分别是谁,即r[ar] = bl, l[bl] = ar。
最后找到最左节点通过r数组遍历输出即可。

启发式合并解法:
使用并查集v数组记录每个集合的根节点,vector数组a按顺序记录每个根节点所包含的元素。
合并集合时将小的集合接在大的集合下(保证nlogn),并将vector合并进大的集合。

AC代码

并查集:

#include <stdio.h>
#include <bits/stdc++.h>
#define fst first
#define sed second
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int N = 2e5 + 10;
int vl[N], vr[N]; //集合最左最右节点 以最左最右节点为根
int l[N], r[N]; //节点左侧右侧节点

inline int find(int *v, int x)
{
	return v[x] == x ? x : v[x] = find(v, v[x]);
}
int main()
{
#ifdef LOCAL
	freopen("C:/input.txt", "r", stdin);
#endif
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++)
		vl[i] = vr[i] = l[i] = r[i] = i;
	for (int i = 1; i < n; i++)
	{
		int a, b;
		scanf("%d%d", &a, &b); //aaaabbbb
		int al = find(vl, a), ar = find(vr, a);
		int bl = find(vl, b), br = find(vr, b);
		r[ar] = bl, l[bl] = ar; //为拼接点记录左右节点
		vr[ar] = br, vl[bl] = al; //合并集合
	}
	int k = find(l, 1); //最左节点
	for (int j = 1; j <= n; j++)
	{
		printf("%d ", k);
		k = r[k];
	}
	cout << endl;

	return 0;
}

启发式合并:

#include <stdio.h>
#include <bits/stdc++.h>
#define fst first
#define sed second
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int N = 2e5 + 10;
vector<int> a[N];
int v[N];

int find(int x)
{
	return v[x] == x ? x : v[x] = find(v[x]);
}
void join(int x, int y)
{
	x = find(x), y = find(y); //得到根节点
	if (a[x].size() > a[y].size())
		swap(x, y);
	v[x] = y; //将小的接在大的下面
	a[y].insert(a[y].end(), a[x].begin(), a[x].end()); //将元素接在y的右边
	a[x].clear();
}
int main()
{
#ifdef LOCAL
	freopen("C:/input.txt", "r", stdin);
#endif
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++)
		v[i] = i, a[i].push_back(i);
	for (int i = 1; i < n; i++)
	{
		int u, v;
		scanf("%d%d", &u, &v);
		join(u, v);
	}
	int r = find(1); //只剩一个集合
	for (int x : a[r])
		printf("%d ", x);
	cout << endl;

	return 0;
}

猜你喜欢

转载自blog.csdn.net/CaprYang/article/details/87921639