1 条题解
-
0
题目理解
我们有一个无向图,每条边的权值可以表示为 。
对于每个查询 ,需要判断是否存在一条从 到 的路径,使得路径上所有边权的最小公倍数恰好等于 。关键点:
- 路径可以不是简单路径(可以重复经过边和点)
- 边权只有两个质因子: 和
- 最小公倍数 = 所有边权中 的最大指数 × 所有边权中 的最大指数
问题转化
设路径经过的边集为 ,则: [ \text{lcm}(E') = 2^{\max_{e \in E'} a_e} \cdot 3^{\max_{e \in E'} b_e} ] 因此,问题转化为:
是否存在一条 到 的路径,使得路径上边的 最大值等于 , 最大值等于
暴力方法的困难
直接对每个查询 BFS/DFS:
- ,
- 每个查询 会超时
- 总复杂度 不可行
离线处理 + 并查集
我们可以使用离线处理和并查集来高效回答所有查询。
核心思路:
将边和查询都按某种顺序处理,利用并查集维护连通性以及每个连通分量的 。
算法设计:双关键字分块
由于有两个维度 和 ,我们可以:
- 对边按 排序,对查询也按 排序
- 将边分成若干块,每块内 值相近
- 对每块内的边,再按 排序
- 使用回滚莫队的思想处理查询
具体步骤:
步骤1:预处理
- 将边按 排序
- 将查询按 排序
- 设定块大小 或根据实际情况调整
步骤2:处理每个 块
对于当前 块 :
- 找出所有 查询值在该块内的查询
- 将这些查询按 排序
- 初始化并查集(包含 的所有边)
- 按 递增顺序处理查询:
- 添加 当前查询 的边(在当前 块内)
- 检查 , 是否连通,且连通分量的 是否等于查询的
- 回滚并查集到当前状态
并查集维护的信息
每个连通分量需要记录:
- 父节点信息(用于路径压缩)
- 连通分量的 ,
- 大小(用于按秩合并)
合并两个连通分量时:
max_a_new = max(comp1.max_a, comp2.max_a) max_b_new = max(comp1.max_b, comp2.max_b)
回滚莫队实现
由于需要回滚操作,我们使用可回滚并查集:
- 记录每次合并操作
- 回滚时逆向执行这些操作
struct DSU { vector<int> parent, size; vector<pair<int, int>> max_ab; // (max_a, max_b) vector<tuple<int, int, int, int>> history; // 操作记录 DSU(int n) : parent(n), size(n, 1), max_ab(n, {-1, -1}) { for (int i = 0; i < n; i++) parent[i] = i; } int find(int x) { while (parent[x] != x) x = parent[x]; return x; } bool unite(int x, int y, int a, int b) { x = find(x), y = find(y); if (x == y) { // 更新最大值 auto [old_a, old_b] = max_ab[x]; max_ab[x] = {max(old_a, a), max(old_b, b)}; history.emplace_back(-1, x, old_a, old_b); // 记录回滚信息 return false; } if (size[x] < size[y]) swap(x, y); // 记录合并前状态 history.emplace_back(y, x, max_ab[x].first, max_ab[x].second); parent[y] = x; size[x] += size[y]; max_ab[x] = { max({max_ab[x].first, max_ab[y].first, a}), max({max_ab[x].second, max_ab[y].second, b}) }; return true; } void rollback(int checkpoint) { while (history.size() > checkpoint) { auto [y, x, old_a, old_b] = history.back(); history.pop_back(); if (y == -1) { // 回滚最大值更新 max_ab[x] = {old_a, old_b}; } else { // 回滚合并操作 parent[y] = y; size[x] -= size[y]; max_ab[x] = {old_a, old_b}; } } } int get_checkpoint() { return history.size(); } };
复杂度分析
- 排序:
- 分块: 块
- 每块处理:
- 总复杂度:,可接受
样例验证
输入:
4 5 1 2 1 3 1 3 1 2 1 4 2 1 2 4 3 2 3 4 2 2 5 1 4 3 3 4 2 2 3 1 3 2 2 2 3 2 2 1 3 4 4处理过程:
- 对于查询 :存在路径 ,最大 ,最大 →
Yes - 对于查询 :无法同时达到 和 →
No
输出:
Yes Yes Yes No No符合样例。
优化技巧
- 块大小调整:根据 和 的关系调整块大小
- 路径压缩优化:在可回滚并查集中使用按秩合并而非路径压缩
- 查询预处理:提前过滤明显不可能的查询
总结
本题的关键在于:
- 将最小公倍数问题转化为双关键字最大值问题
- 使用离线处理 + 分块降低复杂度
- 实现可回滚并查集支持查询回滚
- 注意边界条件和特殊情况的处理
这是一个典型的数据结构+离线算法问题,考察了对复杂查询处理和数据结构的综合应用能力。
- 1
信息
- ID
- 4593
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 3
- 已通过
- 1
- 上传者