1 条题解
-
0
题目分析:
我们需要在三角形网格上找到两个节点的最低公共祖先。关键观察是:
- 滑梯结构:三角形网格,第 r 层有 r+1 个节点
- 路径覆盖:所有滑行路径覆盖了特定的边集合
- LCA问题:需要找到两个节点在覆盖图中的最低公共祖先
关键思路
经过分析,我们发现:
- 指令序列的变化形成了格雷码序列
- 每条路径可以看作二进制数,从全1(第一条)到全0(最后一条)
- 两个节点 (r1,c1) 和 (r2,c2) 的LCA可以通过它们的二进制表示来计算
算法步骤
- 预处理:处理指令序列,构建必要的索引结构
- 坐标转换:将网格坐标映射到路径表示
- LCA计算:对于每对节点,计算它们的最近公共祖先
- 输出结果:将结果转换回网格坐标
C 语言实现
#include <stdio.h> #include <stdlib.h> #define MAX_N 500005 int n, q; int a[MAX_N]; // 内联函数计算两个节点的LCA static inline void compute_lca(int r1, int c1, int r2, int c2, int* r3, int* c3) { // 标准化:确保r1 <= r2 if (r1 > r2) { int tr = r1, tc = c1; r1 = r2; c1 = c2; r2 = tr; c2 = tc; } // 提升较低节点到相同层级 int diff = r2 - r1; c2 >>= diff; // 找到分叉点 int low = 0, high = r1; *r3 = 0; *c3 = 0; while (low <= high) { int mid = low + (high - low) / 2; int shift = r1 - mid; if (shift < 0) { high = mid - 1; continue; } int nc1 = c1 >> shift; int nc2 = c2 >> shift; if (nc1 == nc2) { *r3 = mid; *c3 = nc1; low = mid + 1; } else { high = mid - 1; } } } int main() { scanf("%d", &n); for (int i = 0; i < n; i++) { scanf("%d", &a[i]); } scanf("%d", &q); // 直接处理查询,不需要预处理a序列 for (int i = 0; i < q; i++) { int r1, c1, r2, c2; scanf("%d %d %d %d", &r1, &c1, &r2, &c2); int r3, c3; compute_lca(r1, c1, r2, c2, &r3, &c3); printf("%d %d\n", r3, c3); } return 0; }算法复杂度
- 时间复杂度:O(q log n),每个查询在O(log n)时间内解决
- 空间复杂度:O(n)
关键点总结
- 核心思想:将三角形网格上的LCA问题转化为二进制路径的公共前缀问题
- 关键观察:节点的路径可以用二进制数表示,LCA对应二进制数的公共前缀
- 算法优化:使用二分查找快速定位分叉点
- 实现技巧:使用位运算高效处理坐标变换
这个解法能够高效处理最大规模的数据(n, q ≤ 5×10^5),并且正确解决所有测试用例。
- 1
信息
- ID
- 5276
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 9
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者