当前位置: 首页 > news >正文

种类并查集

简介

种类并查集: 

和基础并查集有很大一部分相同, 多了一个判断2个元素是否属于同一个集团(不是集合, 集合是用来判断2个元素是否能够判断他们属不属于同一个集团:有点绕, 举个例子, 假如知道1和2在不同的集团, 3和4在不同集团,我们就不能判断1和3是否属于一个集团,而集团是用来判断他们是否在同一个集团假如:已知1和2在不同集团,2和3在不同集团, 那么我们就知道1和3在同一个集团);

特色

种类并查集多了一个集团, 我习惯用group[i]来代表i属于哪个集团;注意:group[i]代表的是他和pa[i]的关系, 并不是最后加入所有元素后i属于哪个集团; 
举个例子:我们用0代表group[i]和group[pa[i] 属于同一个集团, 1代表不同; 
现在有pa[3] = 4; group[3] = 1; 这里的group[3]仅仅代表3和4属于属于不同的集团!!!!!(非常重要, 不理解这点你会不能理解种类并查集的!!!), 至于最后3 在哪个集团会在find函数中更新, 下面我会用图示介绍; 
同样分成种类并查集也分成两个:find 和union_set;

因为不同的种类并查集有一定差异:现在用一个例题来说明:POJ1182 http://poj.org/problem?id=1182

做法:每次找x和y是不是在同一个集合中: 
如果是, 判断一下x和y满不满足它所给的关系。 
如果不是, 说明这句话肯定为真:因为另一个集合中的所有元素可以一起变使得它满足x和y的关系; 同时合并两个集合;

当然这么说很简单, 具体做法及解释在下面:

最开始初始化pa[i] = i; group[i] = 0;

  1. find函数: 
    不同的种类并查集有不同的find函数, 不过一般是这样: 
    下面代码中: 
    x 指的是需要查找的数;

    group[x]指的是x和他的父节点(pa[x])的关系:本题中 
    如果group[x] = 0代表x和pa[x]属于同一个物种, 
    如果group[x] = 1代表x吃pa[x], 
    如果group[x] = 2代表x被pa[x]吃;(当然不同的题这里可能不同)

    fa代表的是这个集合的根节点;

    种类并查集的精华1: 
    每次find的时候更新它属于哪个group, 因为他之前的父亲的父亲已经是根节点了, 所以可以利用它之前的父亲和它和之前父亲的关系更新它和根节点的关系,当然更新后的group还是和父亲的关系; 不过父亲已经变成了根节点了;

    find的代码:

    int find(int x) { 
    if(pa[x] == x) return x; 
    int fa = find(pa[x]); 
    group[x] = (group[x] + group[pa[x]]) % 3; 
    return pa[x] = fa; 
    }

  2. unios_set函数: 
    当然这个也要看题, 不过大同小异; 
    代码中的变量解释: 
    fx: x的根节点, fy: y的根节点;

    group[x] : x和它父亲的关系(见上)

    type:指传入的x和y是什么关系, 1代表他们是同类, 2代表x吃y(和题目中是一样的);

pa[x]代表x的父亲;

代码解释:

if(fx == fy) 表示他们属于同一个集合, 因为x和y已经find过了, 所以他们的group都是和根节点的关系(当然他们的根节点是一样的因为fx = fy)这个时候只需要看满不满足type就OK了;

下面是种类并查集的精华2了:

如果他们不是同一个集合, 就链接两个集合;

如果你不是很熟悉, 可以把所有的情况的考虑一下; 
这里提一下, group[fx] = grou[fy] = 0; 因为他们没有父亲, 而我们初始化的时候都初始化成0的; 
比如y和x是同一个group 1.type == 1 那么fx和fy同一类; group[fx] 不变 为 0; 
2.type == 2 因为我们会更新pa[fx] = fy; type == 2 说明x 吃y 本来x 和y 的group是一样的, 所以只要把x的group+1就OK了, 因为x的group加了1, 所以该集合中的所有元素的group必定也+1(他们是一体的) 所以不必更新group[x] 只需要更新group[fx] += 1(因为它的group = 0, 所以直接等于1页可以)就OK了, 这样就实现了该集合的所有元素的group都更新了(因为在find函数的时候会更新group) 
你可以再考虑一下group[y] - grou[x] = 1 后者2 或者-1 或者-2;就会发现他们可以用一个式子满足group[fx] = (group[y] - group[x] + 3 + (type - 1)) % 3;

union_set函数的代码: 
bool union_set(int type, int x, int y) { 
int fx = find(x); 
int fy = find(y); 
if(fx == fy) { 
if(type == 1 && group[x] != group[y]) return true; 
if(type == 2 && (group[y] + 1) % 3 != group[x]) return true; 
return false; 

group[fx] = (group[y] - group[x] + 3 + (type - 1)) % 3; 
pa[fx] = fy; 
return false; 
}

//
//  Created by fkjs on 2015-09-19
//  Copyright (c) 2015 fkjs. All rights reserved.
//
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <sstream>
#include <stack>
#include <vector>
#define clr(x) memset(x, 0, sizeof(x))

using namespace std;

typedef long long int ll;
const int INF = 0x3f3f3f3f;
const int maxn = 5e4 +10;

int pa[maxn], group[maxn];

int find(int x) {
    if(pa[x] == x) return x;
    int fa = find(pa[x]);
    group[x] = (group[x] + group[pa[x]]) % 3;
    return pa[x] = fa;
}

bool union_set(int type, int x, int y) {
    int fx = find(x);
    int fy = find(y);
    if(fx == fy) {
        if(type == 1 && group[x] != group[y]) return true;
        if(type == 2 && (group[y] + 1) % 3 != group[x]) return true;
        return false;
    }
    group[fx] = (group[y] - group[x] + 3 + (type - 1)) % 3;
    pa[fx] = fy;
    return false;
}

int main(void) {
#ifdef LOCAL
    freopen("C:\\Users\\Administrator\\Desktop\\in.txt", "r", stdin);
    freopen("C:\\Users\\Administrator\\Desktop\\out1.txt", "w", stdout);
#endif
    //ios_base::sync_with_stdio(0);

    int n, k;
    scanf("%d%d", &n, &k) == 2;
        for(int i = 1; i <= n; i++) pa[i] = i;
        for(int i = 1; i <= n; i++) group[i] = 0;
        int ans = 0;
        for(int i = 1; i <= k; i++) {
            int x, y, z;
            scanf("%d%d%d", &x, &y, &z);
            if(y > n || z > n || (x == 2 && y == z)){
                ans++;
                continue;
            }
            if(find(y) == find(z)) {
                if(x == 1 && group[y] != group[z]) ans++;
                if(x == 2 && (group[z]+1) % 3 != group[y]) ans++;
            } else {
                if(union_set(x, y, z)) ans++;
            }
        }
        printf("%d\n", ans);

    return 0;
}


相关文章:

  • Sprase-Table(S-T)算法求解RMQ问题
  • LCA(最近公共祖先)问题 (一)
  • 【LCA 倍增法】【codevs 1036 商务旅行】
  • [技巧]读入优化
  • [C++]STL之map
  • 【NOIP 2013 DAY.1】T1 转圈游戏【codevs 3285】
  • 【NOIP 2013 DAY.1】火柴排队【codevs 3286】
  • 归并排序
  • 树状数组求逆序对
  • Linux入门基础 #1:命令行bash基本操作
  • Linux入门基础 #2:Linux文件系统基本结构
  • Linux入门基础 #3:文件基本操作管理和常用命令
  • Linux入门基础 #4:文件系统
  • Linux入门基础 #5:Linux文件系统挂载管理
  • Linux入门基础 #6:Linux用户基础
  • 网络传输文件的问题
  • [译] 怎样写一个基础的编译器
  • 10个最佳ES6特性 ES7与ES8的特性
  • 2017 年终总结 —— 在路上
  • JAVA 学习IO流
  • Java程序员幽默爆笑锦集
  • JAVA之继承和多态
  • leetcode46 Permutation 排列组合
  • rc-form之最单纯情况
  • Spark RDD学习: aggregate函数
  • vuex 笔记整理
  • 分布式熔断降级平台aegis
  • 基于Mobx的多页面小程序的全局共享状态管理实践
  • 记一次和乔布斯合作最难忘的经历
  • 我的zsh配置, 2019最新方案
  • 1.Ext JS 建立web开发工程
  • 扩展资源服务器解决oauth2 性能瓶颈
  • 如何通过报表单元格右键控制报表跳转到不同链接地址 ...
  • ​520就是要宠粉,你的心头书我买单
  • ​TypeScript都不会用,也敢说会前端?
  • #在 README.md 中生成项目目录结构
  • (超详细)2-YOLOV5改进-添加SimAM注意力机制
  • (力扣记录)1448. 统计二叉树中好节点的数目
  • (十一)JAVA springboot ssm b2b2c多用户商城系统源码:服务网关Zuul高级篇
  • (图)IntelliTrace Tools 跟踪云端程序
  • (心得)获取一个数二进制序列中所有的偶数位和奇数位, 分别输出二进制序列。
  • (原创)boost.property_tree解析xml的帮助类以及中文解析问题的解决
  • *(长期更新)软考网络工程师学习笔记——Section 22 无线局域网
  • . NET自动找可写目录
  • .bat批处理(九):替换带有等号=的字符串的子串
  • .bat批处理(六):替换字符串中匹配的子串
  • .NET MVC 验证码
  • .net 打包工具_pyinstaller打包的exe太大?你需要站在巨人的肩膀上-VC++才是王道
  • .Net 路由处理厉害了
  • .Net 中的反射(动态创建类型实例) - Part.4(转自http://www.tracefact.net/CLR-and-Framework/Reflection-Part4.aspx)...
  • .NET6使用MiniExcel根据数据源横向导出头部标题及数据
  • /usr/lib/mysql/plugin权限_给数据库增加密码策略遇到的权限问题
  • @autowired注解作用_Spring Boot进阶教程——注解大全(建议收藏!)
  • @selector(..)警告提示
  • @transactional 方法执行完再commit_当@Transactional遇到@CacheEvict,你的代码是不是有bug!...