并查集
并查集是经典的图论算法,用来维护点与集合的关系,代码也简洁明了。
给定
N
N
N 个点,有
M
M
M 次操作,每次操作输入
p
p
p
a
a
a
b
b
b 。
若
p
=
1
p = 1
p=1 ,则 合并
a
a
a
b
b
b;
若
p
=
2
p = 2
p=2 ,则 查询
a
a
a
b
b
b 是否同属一个集合;
并查集初始化:每个父亲点 都 存储 儿子的信息:
for(int i = 1; i <= n; i++) fa[i] = i;
并查集的查询(路径压缩版):
int find(int x)
{
if(x == fa[x]) return x;
else return fa[x] = find(fa[x]);
}
并查集的合并:
int x = find(a), y = find(b);
if (x != y) fa[x] = y;
并查集模版完整代码:
#include <iostream>
#include <cstdio>
using namespace std;
int n,m;
int fa[1001000];
int find(int x)
{
if(x == fa[x]) return fa[x];
else return fa[x] = find(fa[x]);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++) fa[i] = i;
for(int i = 1; i <= m; i++)
{
int p,b,c;
scanf("%d%d%d",&p,&b,&c);
int x = find(fa[b]);
int y = find(fa[c]);
if(p == 1) fa[x] = y;
if(p == 2)
{
if(x == y) printf("Y\n");
else printf("N\n");
}
}
return 0;
}
有些特殊的题目需要对并查集进行变形,比如合并的时候统计当前连通块的大小,这时候需要对 查询或者合并操作进行对应修改。
银河英雄传说
[USACO04OPEN]Cube Stacking
例二代码:
#include <iostream>
#include <cstdio>
using namespace std;
int p,a,b;
int fa[33000],sum[33000],dist[33000];
char ch;
int find(int x)
{
if(x == fa[x]) return x;
int k = find(fa[x]);
dist[x] += dist[fa[x]];
return fa[x] = k;
}
void unite(int x, int y)
{
int X = find(x), Y = find(y);
fa[X] = Y;
dist[X] = sum[Y];
sum[Y] += sum[X];
}
int main()
{
scanf("%d",&p);
for(int i = 1; i <= 30000; i++)
sum[i] = 1, fa[i] = i;
for(int i = 1; i <= p; i++)
{
cin >> ch;
if(ch == 'M')
{
cin >> a >> b;
unite(a, b);
}
if(ch == 'C')
{
cin >> a;
int x = find(a);
printf("%d\n",dist[a]);
}
}
return 0;
}
还有一些开多倍并查集的题目,来表示朋友或敌人的关系。如果题目要求给定数据是不在同一个集合,则可以令
u
u
u 和
v
+
n
v+n
v+n 联合在一起。
BOI团伙
NOI食物链
NOIP关押罪犯
例二代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <stack>
#include <queue>
#include <ctime>
#include <set>
using namespace std;
const int Maxn = 50000 + 50;
int fa[Maxn << 2];
int read()
{
int rt = 0, in = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') in = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {rt = rt * 10 + ch - '0'; ch = getchar();}
return rt * in;
}
int find(int x){return x == fa[x] ? x : fa[x] = find(fa[x]);}
void unite(int x, int y){int X = find(x), Y = find(y); fa[X] = Y;}
int main()
{
int n = read(), k = read(), ans = 0;
for (int i = 1; i <= 3*n; ++i) fa[i] = i;
for (int i = 1; i <= k; ++i)
{
int ins = read(), x = read(), y = read();
if ( x > n || y > n || (ins == 2 && x == y) )
{
++ans;
continue;
}
if (ins == 1)
if(find(x+n) == find(y) || find(x+2*n) == find(y) || find(x) == find(y+n) || find(x) == find(y+2*n))
{
++ans;
continue;
}
else unite(x, y), unite(x+n, y+n), unite(x+2*n, y+2*n);
else
if(find(x) == find(y) || find(y) == find(x+n))
{
++ans;
continue;
}
else unite(x, y+n), unite(x+n, y+2*n), unite(x+2*n, y);
}
printf("%d\n",ans);
system("pause");
return 0;
}