洛谷A - 并查集 LibreOJ - 109

题目描述

这是一道模板题。

维护一个 n 点的无向图,支持:

  • 加入一条连接 u 和 v 的无向边
  • 查询 u 和 v 的连通性

由于本题数据较大,因此输出的时候采用特殊的输出方式:用 00 或 11 代表每个询问的答案,将每个询问的答案依次从左到右排列,把得到的串视为一个二进制数,输出这个二进制数 mod 998244353mod 998244353 的值。

请务必使用快读。

输入格式

第一行包含两个整数n,m,表示点的个数和操作的数目。

接下来 m 行每行包括三个整数 op,u,v。

  • 如果 op=0op=0,则表示加入一条连接 u 和 v 的无向边;
  • 如果 op=1op=1,则表示查询 u 和 v 的连通性。

输出格式

一行包括一个整数表示答案。

样例

InputcopyOutputcopy
3 6
1 1 0
0 0 1
1 0 1
1 1 2
0 2 1
1 2 1
5

答案串为 01010101。

数据范围与提示

n≤4000000,m≤8000000

#include <iostream> // 引入输入输出流库
using namespace std; // 使用标准命名空间

const int N = 4000010; // 定义一个常量 N,表示集合的最大大小
int fa[N]; // 创建一个数组 fa,用于存放每个元素的父节点信息
long long ans = 0ll; // 创建一个变量 ans 来存放最终的答案,使用 long long 类型以避免溢出

// 查找函数,用于找到元素 x 所在集合的代表元
int find(int x) {
    if (fa[x] == x) { // 如果 x 是代表元,直接返回
        return x;
    } else
        // 如果 x 不是代表元,递归查找 x 的父节点的代表元,并更新 fa[x]
        return fa[x] = find(fa[x]);
}

int main() {
    int n, m; // n 表示元素的数量,m 表示操作的数量
    scanf("%d%d", &n, &m); // 从标准输入读取 n 和 m
    for (int i = 0; i < n; i++) {
        fa[i] = i; // 初始化时,每个元素都是自己的代表元
    }
    while (m--) { // 处理 m 个操作
        int op, u, v; // op 表示操作类型,u 和 v 表示操作的元素
        scanf("%d%d%d", &op, &u, &v); // 从标准输入读取操作信息
        if (op == 0) { // 如果要合并两个集合
            fa[find(u)] = find(v); // 将 u 的代表元的父节点设置为 v 的代表元
        } else { // 如果要查询两个元素是否在同一个集合
            ans <<= 1; // 将答案左移一位,相当于乘以 2,准备添加下一位查询结果
            if (find(u) == find(v)) // 如果 u 和 v 在同一个集合中
                ans += 1; // 添加 1 到答案中
            ans %= 998244353; // 对答案取模,防止溢出
        }
    }
    printf("%lld\n", ans); // 输出最终的答案
    return 0; // 程序结束
}

并查集(Disjoint Set)是一种用于处理集合合并与查询的数据结构。它维护了一个集合的划分,每个集合通过一个代表元素来表示,可以高效地进行合并和查询两个元素是否在同一个集合中的操作。

代码中使用一个数组 fa 来存放每个元素的父节点信息,初始时,每个元素都是自己的代表元。通过 find 函数来查找元素所在集合的代表元,该函数使用了路径压缩的优化技巧,在查找过程中会将经过的节点的父节点直接更新为集合的代表元,以提高后续查询操作的效率。

在主函数中,首先从输入中读取元素的数量 n 和操作的数量 m。然后使用一个循环来处理 m 个操作。每个操作由操作类型 op 和操作元素 uv 组成。如果操作类型为 0,表示要合并两个集合,则将以 uv 为代表元的集合进行合并,即将 u 的父节点设置为 v;如果操作类型为 1,表示要查询两个元素是否在同一个集合中,则根据 find 函数的返回值判断。

在查询操作中,为了方便计算答案,将答案 ans 左移一位,并根据查询结果判断是否将 1 添加到答案中。最后,输出答案。

需要注意的是,代码中对答案 ans 进行了取模操作,防止溢出。

 

  • 8
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值