#并查集 20.09.25

并查集

AcWing 836. 合并集合

题目

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. 连通块中点的数量

题目

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. 食物链

题目

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;
	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值