1 条题解
-
0
我分析了题目「和 Grundy 玩游戏」和您提供的代码。这道题主要考察几何建模与离散化扫描线思想。下面是我的分析。
算法思路解析
问题转化
朋友 在 ,视野是斜率为 的向上射线(不包括射线本身)构成的三角形区域。小G藏在 ,其中 且为整数。
对于朋友 ,点 在其视野内部的条件是严格在两条射线之间:
$$x_i - \frac{h_i \cdot Y}{v_i} < a < x_i + \frac{h_i \cdot Y}{v_i} $$注意边界不包含,因为视野不包括射线本身。
核心算法:差分数组 + 扫描线
代码使用了离散化和差分数组的技巧:
-
计算每个朋友的视野区间:
int t = y * h; int b = x + ((t % v == 0) ? (t / v) : (t / v + 1)); int a = x - ((t % v == 0) ? (t / v - 1) : (t / v));这里
a是左端点(开区间),b是右端点(开区间)。处理确保了开区间和整数坐标的对应。 -
事件点标记:
- 将每个区间拆成两个事件:在
a处标记开始(覆盖数+1),在b处标记结束(覆盖数-1) - 使用
pair<int, int>存储,第二项为 -1 表示开始,+1 表示结束
- 将每个区间拆成两个事件:在
-
扫描线处理:
- 对所有事件点按坐标排序
- 从左到右扫描,维护当前区域的覆盖数
total - 统计每个覆盖数对应的横坐标数量
代码分析
关键部分解析
// 计算每个朋友产生的视野区间 int t = y * h; int b = x + ((t % v == 0) ? (t / v) : (t / v + 1)); int a = x - ((t % v == 0) ? (t / v - 1) : (t / v)); // 存储为事件点 lines[i*2-1] = make_pair(a, -1); // 开始 lines[i*2] = make_pair(b, 1); // 结束// 扫描线处理 int total = 0; lines[n*2+1].first = r+1; ans[0] += lines[1].first - l; // 最开始未被覆盖的部分 for(int i = 1; i <= (n<<1); i++){ total += -lines[i].second; // 更新覆盖数 ans[total] += lines[i+1].first - lines[i].first; // 统计该覆盖数对应的横坐标数 }复杂度分析
- 时间复杂度:,主要来自对 个事件点的排序
- 空间复杂度:,存储事件点和答案数组
算法标签
主要分类
- 离散化 ⭐⭐⭐⭐
- 扫描线算法 ⭐⭐⭐⭐
- 几何计算 ⭐⭐⭐
技术子标签
- 差分数组 ⭐⭐⭐⭐
- 事件点处理 ⭐⭐⭐⭐
- 区间合并 ⭐⭐⭐
问题类型
- 计数问题 ⭐⭐⭐
- 坐标处理 ⭐⭐⭐⭐
样例验证
对于样例输入:
3 -7 7 3 0 2 3 -4 2 1 3 3 1代码计算过程:
- 计算每个朋友的视野区间
- 生成事件点并排序
- 扫描统计每个覆盖数对应的横坐标数量
- 输出:5, 12, 15, 15
算法优势
- 高效处理大范围:利用离散化,在 达到 时仍能高效工作
- 避免复杂几何判断:将几何问题转化为区间覆盖问题
- 输出完整答案:直接得到覆盖数为 到 的所有情况
该解法通过巧妙的事件点建模和扫描线技术,将复杂的几何包含判断转化为简洁的区间统计问题,是大范围几何计数问题的经典解法。
-
- 1
信息
- ID
- 4114
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者