实现方法
在初始化并查集时我们初始化两倍于数据范围大小的并查集,超出数据范围部分称为反集,用来储存性质于并查集维护集合相反的集合。并且两个集合中的元素性质互斥。
并查集的反集适用于只有元素只有两种性质的题目,也就是说,这个元素不属于并查集维护集合,则其必定属于另一个集合。如果我们要将两个性质不同的元素a,b合并,我么们可以用并查集合并 a+n,和 b b+n和 a, 加一个n即可将元素的一个虚拟敌人存入反集,并且于另一个元素合并。在之如果 c 与 a的性质不同,我们将 a+n 与 c和并,c+n 与 a 合并,此时 b与a 性质相同,他们成功合并在一起。
经典例题
一个非常经典的用处就是用来判断二分图,在时间复杂的上比bfs和dfs染色法要优秀很多
Acwing860. 染色法判定二分图https://www.acwing.com/problem/content/862/
ACcode
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int long long
#define endl '\n'
#define lowbit(x) x &(-x)
#define mh(x) memset(x, -1, sizeof h)
#define debug(x) cerr << #x << "=" << x << endl;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
int read()
{
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
x = x * 10 + c - '0', c = getchar();
return x * f;
}
int p[N];
int find(int x)
{
if (x != p[x])
p[x] = find(p[x]);
return p[x];
}
signed main()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n * 2; i++)
p[i] = i;
int f = 1;
while (m--)
{
int a, b;
cin >> a >> b;
a = find(a), b = find(b);
int x = find(a + n), y = find(b + n);
if (a == b)
f = 0;
else
{
p[x] = p[b];
p[y] = p[a];
}
}
if (f)
cout << "Yes" << endl;
else
cout << "No" << endl;
return 0;
}
P1892 [BOI2003]团伙https://www.luogu.com.cn/problem/P1892
ACcode
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int long long
#define endl '\n'
#define lowbit(x) x &(-x)
#define mh(x) memset(x, -1, sizeof h)
#define debug(x) cerr << #x << "=" << x << endl;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
int read()
{
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
x = x * 10 + c - '0', c = getchar();
return x * f;
}
int p[N];
int find(int x)
{
if (x != p[x])
p[x] = find(p[x]);
return p[x];
}
void merge(int a, int b)
{
a = find(a), b = find(b);
if (a != b)
{
p[a] = p[b];//注意,此处合并一定要将父指针指到范围n以内
}
}
signed main()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n * 2; i++)
p[i] = i;
while (m--)
{
string op;
int a, b;
cin >> op >> a >> b;
if (op == "E")
{
merge(a + n, b);
merge(b + n, a);
}
else if (op == "F")
{
merge(a, b);
}
}
int res = 0;
for (int i = 1; i <= n; i++)
{
if (p[i] == i)
res++;
}
cout << res << endl;
return 0;
}