2035.5.15 并查集
并查集在加入了路径压缩的优化后支持以近乎o(1)来进行两个操作 :
1.合并两个集合
2.判断两个元素是否属于同一个集合
一开始有n个数,后面有n1个合并集合操作,n2个查询两个元素是否在同一个集合操作
//n个数,最开始每个数各自在一个集合中
#include <iostream>using namespace std;const int N = 100010;int p[N]; //p的意思是pre
int n, n1, n2;int find(int x) //在并查集基本操作中可以维护一些额外的变量
{while (p[x] != x) p[x]=find(p[x]); //如果这里是x=p[x]那就不带路径压缩 如果是p[x]=find(p[x])那么就加入了路径压缩 这里难懂的原因这里find(p[x])的意思是下一层递归将p[x]当成下一层的x,那么就实现了向上找的功能return x;
}int main()
{cin >> n >> n1 >> n2;for (int i = 1; i <= n; i++)p[i] = i;while (n1--){int a, b;cin >> a >> b;p[find(a)] = find(b);}while (n2--){int a, b;cin >> a >> b;if (find(a) == find(b)) cout << "yes" << endl;else cout << "no" << endl;}return 0;
}
同时除了完成并查集两个这种最基础的操作,它还支持在完成基础操作的过程中维护一些额外的白变量,比如每个集合的元素数量大小
下面代码题意与上面相同,只不过多了n3个查询元素a所在集合的元素数量的操作
#include <iostream>using namespace std;const int N = 100010;int p[N],s[N]; //s的意思是size 规定只有根节点(代表元)有意义
int n, n1, n2, n3;int find(int x)
{while (p[x] != x) p[x] = find(p[x]);return x;
}int main()
{cin >> n >> n1 >> n2 >> n3;for (int i = 1; i <= n; i++){s[i] = 1;p[i] = i;}while (n1--){int a,b;cin >> a >> b;if (find(a) == find(b)) continue;p[find(a)] = find(b);s[find(b)] += s[find(a)]; //如果上面没有判断a和b是否已经在一个集合中了,那么s会多加,导致出现错误}while (n2--){int a, b;cin >> a >> b;if (find(a) == find(b)) cout << "yes" << endl;else cout << "no" << endl;}while (n3--){int a;cin >> a;cout << s[find(a)] << endl;}return 0;
}