1 基础复习
- 主要功能:
- 查询两个元素是否在一个集合中
- 合并两个集合(单独的一个元素也算一个集合)
- 查询连通块中点的数量
- 时间复杂度:
具体问题具体分析- 基本思路:
- 用一个p数组维护每个节点的父节点,例如x节点的父节点为p[x]
- 利用find函数进行集合查询和路径压缩,例如find(x)返回x的集合
- 每个集合的顶点元素值用来表示该集合
- 合并两个集合可以让其中一个集合顶点x的p[x]等于另一个集合的顶点元素值。
- 因为所有集合的顶点元素值都不同,所以可以维护功能数组来存储所有集合的信息,例如一个集合中节点的数量(连通块中点的数量)
模板题1: Acwing. 836. 合并集合
#include<iostream>
using namespace std;
const int N = 1e5 + 5;
int p[N], n, m;
//查询一个节点x的集合顶点值并压缩路径
int find(int x)
{
if(x != p[x])
return p[x] = find(p[x]);
return x;
}
int main()
{
cin >> n >> m;
//初始化所有节点的集合顶点值为本身
for(int i = 1; i <= n; i++)
p[i] = i;
while(m--)
{
char op;
int a, b;
cin >> op >> a >> b;
int pa = find(a), pb = find(b);
if(op == 'M')
p[pa] = pb;
else
if(pa == pb)puts("Yes");
else puts("No");
}
return 0;
}
模板题2: Acwing837. 连通块中点的数量
#include<iostream>
using namespace std;
const int N = 1e5 + 5;
int p[N], cnt[N];
int n, m;
int find(int x)
{
if(x != p[x])
return p[x] = find(p[x]);
return x;
}
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i++)
{
p[i] = i;
cnt[i] = 1;
}
while(m--)
{
string op;
int a, b;
cin >> op;
if(op == "C")
{
cin >> a >> b;
int pa = find(a), pb = find(b);
if(pa == pb)continue;
cnt[pa] += cnt[pb];
p[pb] = pa;
}
else if(op == "Q1")
{
cin >> a >> b;
int pa = find(a), pb = find(b);
if(pa == pb)
puts("Yes");
else
puts("No");
}
else
{
cin >> a;
cout << cnt[find(a)] << endl;
}
}
return 0;
}
模板题3: Acwing240. 食物链
#include<iostream>
using namespace std;
const int N = 5e4 + 5;
int p[N], dist[N], n, k;
int find(int x)
{
if(x != p[x])
{
int t = find(p[x]);
dist[x] += dist[p[x]];
p[x] = t;
}
return x;
}
int main()
{
cin >> n >> k;
for(int i = 1; i <= n; i++)
p[i] = i;
int res = 0;
while(k--)
{
int x, y, op;
cin >> op >> x >> y;
int px = find(x), py = find(y);
if(x > n || y > n)
{
res++;
continue;
}
else if(op == 1)
{
if(px == py && (dist[x] - dist[y]) % 3)res++;
else if(px != py)
{
p[px] = py;
dist[px] = dist[y] - dist[x];
}
}
else
{
if(px == py && (dist[x] - 1 - dist[y]) % 3)res++;
else if(px != py)
{
p[px] = py;
dist[px] = dist[y] - dist[x] + 1;
}
}
}
cout << res;
return 0;
}
: