1 条题解
-
0
解题思路
本题要求计算每只鸟在飞行时首次撞击的墙的次数。关键在于分析鸟的飞行方向与墙的位置关系,利用线段树优化查询过程:
核心步骤:
- 墙的处理:将墙按水平和垂直分类,记录其坐标范围。
- 坐标离散化:对墙和鸟的坐标进行离散化,以便线段树处理。
- 线段树操作:
- 按y坐标排序墙,用线段树维护x坐标区间,查询鸟在垂直方向飞行时最近的墙。
- 按x坐标排序墙,用线段树维护y坐标区间,查询鸟在水平方向飞行时最近的墙。
- 方向选择:鸟选择四个方向中最早撞击墙的方向,统计每面墙的撞击次数。
题意分析
问题本质:
- 输入:N面与坐标轴平行的墙,M只鸟的位置。
- 输出:每面墙被鸟撞击的次数。
关键点:
- 墙的特征:墙为水平或垂直的线段,不相交。
- 鸟的飞行:鸟向四个轴向方向飞行,选择最早撞击墙的方向。
- 撞击判定:鸟的飞行路径与墙的线段相交时视为撞击。
标程实现
#include <cstdio> #include <cstring> #include <algorithm> #define rep(i,j,k) for(int i=j;i<k;++i) #define ms(i,j,k) memset(i,j,sizeof(int)*k) using namespace std; const int N = 50005, S = N * 3; int wall, tot; int dis[N], v[N], w[N], c[S * 3]; int x[S], y[S], rx[S], ry[S], xs[S], ys[S], px[S], py[S]; int *num; bool cmp(int a, int b) { return num[a] < num[b]; } // 坐标离散化 void relabel(int *x, int *r, int tot, int *a, int *mp) { int k, p, m = 0; rep(i,0,tot) r[i] = i; num = x; sort(r, r + tot, cmp); a[r[0]] = ++m; mp[m] = x[r[0]]; rep(i,1,tot) { k = r[i]; p = r[i - 1]; if (x[k] != x[p]) a[k] = ++m, mp[m] = x[k]; else a[k] = m; } } // 线段树查询:获取指定位置的标记 int get(int t, int l, int r, int q) { if (c[t] != -2) return c[t]; int mid = l + r >> 1; if (q <= mid) return get(t * 2, l, mid, q); else return get(t * 2 + 1, mid + 1, r, q); } // 线段树更新:区间标记 void set(int t, int l, int r, int ql, int qr, int qc) { if (ql <= l && r <= qr) { c[t] = qc; return; } if (c[t] != -2) { c[t * 2] = c[t * 2 + 1] = c[t]; c[t] = -2; } int mid = l + r >> 1; if (ql <= mid) set(t * 2, l, mid, ql, qr, qc); if (qr > mid) set(t * 2 + 1, mid + 1, r, ql, qr, qc); } // 扫描鸟的位置,更新最近撞击墙的信息 void scan(int k, int *ys, int *xs, int *py, int n) { if (k < wall) { // 处理垂直墙 int k2 = k ^ 1; if (xs[k2] >= xs[k]) set(1, 1, n, xs[k], xs[k2], k / 2); } else { // 处理鸟的位置 int t = get(1, 1, n, xs[k]); if (~t) { int d = min(abs(py[ys[k]] - py[ys[t * 2]]), abs(py[ys[k]] - py[ys[t * 2 + 1]])); k -= wall; if (d < dis[k]) dis[k] = d, v[k] = t; } } } // 处理鸟在水平/垂直方向的飞行 void fly(int *ry, int *ys, int *xs, int *py, int n) { c[1] = -1; rep(i,0,tot) scan(ry[i], ys, xs, py, n); c[1] = -1; for(int i=tot-1;i>=0;--i) scan(ry[i], ys, xs, py, n); } int main() { int n, m; scanf("%d%d", &n, &m); wall = n * 2; tot = wall + m; // 读取墙和鸟的坐标 rep(i,0,tot) scanf("%d%d", x + i, y + i); // 离散化x坐标 relabel(x, rx, tot, xs, px); // 离散化y坐标 relabel(y, ry, tot, ys, py); // 初始化距离数组和计数数组 ms(dis, 0x7f, m); ms(w, 0, n); // 处理垂直方向飞行,查询水平墙 fly(ry, ys, xs, py, xs[rx[tot - 1]]); // 处理水平方向飞行,查询垂直墙 fly(rx, xs, ys, px, ys[ry[tot - 1]]); // 统计每面墙的撞击次数 rep(i,0,m) ++w[v[i]]; // 输出结果 rep(i,0,n) printf("%d\n", w[i]); return 0; }
代码解释
-
坐标离散化:
relabel
函数将墙和鸟的坐标映射到连续整数,减少线段树处理范围。- 排序后去重,生成坐标到排名的映射表。
-
线段树操作:
set
函数用于区间标记,记录墙的x/y坐标范围。get
函数查询指定位置的标记,确定最近的墙。
-
飞行方向处理:
fly
函数分两次扫描(正序和逆序),处理鸟在水平/垂直方向的飞行。- 通过线段树快速定位最近的墙,更新鸟的撞击记录。
-
结果统计:
v[i]
记录第i只鸟撞击的墙,w数组
统计每面墙的撞击次数。
关键逻辑
- 方向选择:鸟选择四个方向中飞行距离最短的方向,确保首次撞击的墙唯一。
- 线段树优化:通过区间标记和查询,高效处理大量墙和鸟的位置关系。
- 离散化处理:将大范围坐标映射到小区间,减少线段树节点数,提高效率。
- 1
信息
- ID
- 2471
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 4
- 已通过
- 1
- 上传者