1 条题解
-
0
PA 2019 Runda 3 Terytoria 题解
题目分析
这道题要求在一个二维环面(即左右边界连通、上下边界连通的地图)上,找到被所有矩形覆盖的格子的最大可能数量。
关键点:
- 地图是环面结构,需要考虑边界情况
- 矩形边平行于坐标轴
- 需要找到所有矩形的最大公共覆盖区域
解题思路
核心观察
- 环面性质:由于地图是环面,矩形可以跨越边界
- 最大公共交集:问题转化为找到所有矩形在环面上的最大公共交集
- 随机化哈希:使用随机哈希来标识不同的覆盖区域
算法设计
代码采用了坐标离散化 + 随机哈希 + 树状数组的方法:
主要步骤:
- 坐标离散化:将原始坐标映射到离散的索引
- 随机哈希:为每个矩形分配随机哈希值
- 差分更新:使用树状数组记录每个区间的哈希值
- 统计最大区间:统计相同哈希值的区间长度总和,找到最大值
代码详解
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; typedef __int128 i128; typedef pair<int, int> pii; const int N = 1e6 + 10; int val1[N], val2[N], n, m1, m2; struct P { int l, r; } a[N], b[N]; struct Tree { int tot; ull tree[N]; void change(int x, ull s) { while (x <= tot) { tree[x] ^= s; x += x & -x; } } ull ask(int x) { ull sum = 0; while (x) { sum ^= tree[x]; x -= x & -x; } return sum; } } tree1, tree2; unordered_map<ull, int> mp; int main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin >> n >> m1 >> m2; // 读入矩形坐标 for (int i = 1; i <= n; i++) cin >> a[i].l >> b[i].l >> a[i].r >> b[i].r; // 坐标离散化 - 收集所有坐标值 val1[++tree1.tot] = 0, val1[++tree1.tot] = m1; val2[++tree2.tot] = 0, val2[++tree2.tot] = m2; for (int i = 1; i <= n; i++) { val1[++tree1.tot] = a[i].l, val1[++tree1.tot] = a[i].r; val2[++tree2.tot] = b[i].l, val2[++tree2.tot] = b[i].r; } // 排序并去重 sort(val1 + 1, val1 + tree1.tot + 1); sort(val2 + 1, val2 + tree2.tot + 1); tree1.tot = unique(val1 + 1, val1 + tree1.tot + 1) - val1 - 1; tree2.tot = unique(val2 + 1, val2 + tree2.tot + 1) - val2 - 1; // 将原始坐标映射到离散索引 for (int i = 1; i <= n; i++) { a[i].l = lower_bound(val1 + 1, val1 + tree1.tot + 1, a[i].l) - val1; a[i].r = lower_bound(val1 + 1, val1 + tree1.tot + 1, a[i].r) - val1; b[i].l = lower_bound(val2 + 1, val2 + tree2.tot + 1, b[i].l) - val2; b[i].r = lower_bound(val2 + 1, val2 + tree2.tot + 1, b[i].r) - val2; } // 生成随机哈希值并更新树状数组 srand(time(0)); for (int i = 1; i <= n; i++) { ull s = 1ll * rand() * rand() * rand() * rand(); // 随机哈希值 tree1.change(a[i].l, s); tree1.change(a[i].r, s); tree2.change(b[i].l, s); tree2.change(b[i].r, s); } // 统计x方向的最大公共区间 int c1 = 0, c2 = 0; for (int i = 1; i < tree1.tot; i++) mp[tree1.ask(i)] += val1[i + 1] - val1[i]; for (auto v : mp) c1 = max(c1, v.second); // 统计y方向的最大公共区间 mp.clear(); for (int i = 1; i < tree2.tot; i++) mp[tree2.ask(i)] += val2[i + 1] - val2[i]; for (auto v : mp) c2 = max(c2, v.second); // 输出结果 cout << 1ll * c1 * c2 << '\n'; return 0; }算法原理
为什么这样是正确的?
-
哈希值表示覆盖模式:
- 每个矩形分配一个随机哈希值
- 树状数组记录每个位置的累积哈希值
- 相同哈希值的位置表示被相同的矩形集合覆盖
-
差分更新:
- 在矩形开始位置添加哈希值
- 在矩形结束位置再次添加相同的哈希值(异或操作抵消)
- 这样区间内的哈希值表示该区间被哪些矩形覆盖
-
环面处理:
- 算法天然支持环面结构,因为哈希值的计算不依赖于绝对位置
- 跨越边界的矩形会被正确处理
关键技巧
- 坐标离散化:将大范围坐标映射到小范围索引,减少空间复杂度
- 随机哈希:使用随机数避免哈希冲突,保证正确性
- 树状数组:高效支持区间更新和单点查询
样例分析
对于样例输入:
2 10 7 2 1 8 6 5 2 4 4算法会:
- 离散化x坐标和y坐标
- 为两个矩形生成随机哈希值
- 计算x方向和y方向的最大公共覆盖区间
- 输出面积乘积 15
复杂度分析
- 时间复杂度:,主要来自排序和树状数组操作
- 空间复杂度:,存储离散化后的坐标和树状数组
总结
这道题的解题亮点:
- 巧妙的哈希设计:用随机哈希标识覆盖模式
- 差分技巧:高效处理区间覆盖问题
- 离散化优化:处理大坐标范围
- 环面适应性:算法天然支持环面结构
这种"哈希+差分"的方法在解决多个区间的公共交集问题时非常有效,特别是在需要处理复杂空间结构的情况下。
- 1
信息
- ID
- 5009
- 时间
- 3000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者