并查集
AcWing 836. 合并集合
题目
思路
并查集
答案
#include <iostream>
using namespace std;
const int N = 100010;
int p[N];
int find(int x)
{
return p[x] == x ? p[x] : p[x] = find(p[x]);
//这句活浓缩了很多东西哦
//首先会把祖先返回;
//其次还会将每一个点的父节点都变为祖先。
}
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ ) p[i] = i;
while (m -- )
{
char op[2];
int a, b;
scanf("%s%d%d", op, &a, &b);
if (*op == 'M') p[find(a)] = find(b);
else
{
if (find(a) == find(b)) puts("Yes");
else puts("No");
}
}
return 0;
}
AcWing 837. 连通块中点的数量
题目
思路
就多了一个算和的情况嘛,
每次只需要将一个 祖宗所在并查集的点数加上 另一个 祖宗 所在并查集的点数就好了;
答案
#include <iostream>
using namespace std;
const int N = 100010;
int p[N];
int sum[N];
int a, b;
string op;
int find(int x){
return p[x] == x ? p[x] : p[x] = find(p[x]);
}
int main(){
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ ) p[i] = i, sum[i] = 1;
while (m -- )
{
cin >> op;
if(op == "C"){
scanf("%d%d", &a, &b);
if(a != b){
int pa = find(a), pb = find(b);
if(pa != pb) sum[pb] += sum[pa], p[pa] = pb;
}
}
else{
if(op == "Q1"){
scanf("%d%d", &a, &b);
if(find(a) == find(b)) printf("Yes\n");
else printf("No\n");
}
else scanf("%d", &a), printf("%d\n", sum[find(a)]);
}
}
return 0;
}
AcWing 240. 食物链
题目
思路
……
题意大概就是,并查集里面的元素每三层分一组,每一组的同一位置 的元素为一类,然后要你判断句子对错;
我们可以用一个d数组来记录这个点到祖宗节点的距离,然后%3 来判断它在某一组的哪一层,以此判断它是哪一类动物;
于是我们只需要额外地记录当前点到祖宗节点的距离就可以了;
如果描述的时候两个点不在同一个并查集之中,那么就合并,
距离的处理就是用两个点合并前到各自祖宗的距离相减,得到的就是一个点的祖宗到合并后的新祖宗的距离;
答案
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e5 + 10;
int sum;
int n, m, P;
int p[N];
int d[N];
int find(int x){
if(x != p[x]){
int t = find(p[x]);
d[x] += d[p[x]];
p[x] = t;
}
return p[x];
}
int main(){
// freopen("ttt.in", "r", stdin);
// freopen("ttt.out", "w", stdout);
int n, k;
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i ++ ) p[i] = i;
while (k -- ){
int z, a, b;
scanf("%d%d%d", &z, &a, &b);
int pa = find(a), pb = find(b);
if(a > n || b > n) sum ++;
else{
if (z == 1){
if(pa == pb && (d[a] - d[b]) % 3 ) sum ++;
else if(pa != pb){
p[pa] = pb;
d[pa] = d[b] - d[a];
}
}
else
{
if(pa == pb && (d[a] - d[b] - 1) % 3) sum ++;
else if(pa != pb){
p[pa] = pb;
d[pa] = d[b] + 1 - d[a];
}
}
}
}
cout << sum;
return 0;
}
第二次 食物链(并查集)
AcWing 240. 食物链
思路
这个有点麻烦,题目描述得太烂了。
……
答案
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e5 + 10;
int sum;
int n, m, P;
int p[N];
int d[N];
//这里每执行一次都会直接将祖宗节点作为所有在内节点的父节点,
//其距离不会重复计算第二次,因为下一次就遍历不到之前的父节点了.
int find(int x){
if(x != p[x]){
int t = find(p[x]);
d[x] += d[p[x]];
p[x] = t;
}
return p[x];
}
int main(){
// freopen("ttt.in", "r", stdin);
// freopen("ttt.out", "w", stdout);
int n, k;
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i ++ ) p[i] = i;
while (k -- ){
int z, a, b;
scanf("%d%d%d", &z, &a, &b);
int pa = find(a), pb = find(b);
//假话
if(a > n || b > n) sum ++;
else{
//1代表同类
if (z == 1){
//(d[a] - d[b]) % 3这里如果不是0就说明他们根本就不是同类的
if(pa == pb && (d[a] - d[b]) % 3 ) sum ++;
//这里用else if 是因为上面的if有两个判断句,
//用else 的话就是肯定了其它三种情况,
//然而我们需要处理的只是四种中的两种
else if(pa != pb){
p[pa] = pb;
//添加的时候由于还没有用到find()函数,所以并不会立即更新新连通块内所有点到其对应根节点的距离
//这时候就要先将之前的根节点到当前根节点的距离处理一下,
//下一次的find()就会更新新连通块内所有点到其根节点的距离了
d[pa] = d[b] - d[a];
}
}
//2代表a吃b
else
{
//假话,说反了
if(pa == pb && (d[a] - d[b] - 1) % 3) sum ++;
else if(pa != pb){
p[pa] = pb;
d[pa] = d[b] + 1 - d[a];
}
}
}
}
cout << sum;
return 0;
}