题解:AtCoder AT_awc0062_e Radio Tower and Signal Strength
【题目来源】
AtCoder:E - Radio Tower and Signal Strength
【题目描述】
Takahashi is an engineer at a telecommunications company, responsible for investigating radio conditions along a road.
高桥是一家电信公司的工程师,负责调查一条道路沿线的无线电状况。
The road under investigation is a straight line, and \(N\) radio towers are built along the road. The road is represented as a number line, where the \(i\)-th radio tower is located at coordinate \(X_i\) and has power output \(B_i\).
调查的道路是一条直线,沿着道路建有 \(N\) 座无线电塔。道路用一条数轴表示,其中第 \(i\) 座无线电塔位于坐标 \(X_i\),输出功率为 \(B_i\)。
The "signal strength" at coordinate \(p\) is defined as the sum of contributions from all radio towers:
坐标 \(p\) 处的"信号强度"定义为所有无线电塔贡献的总和:
\(f(p) = \sum_{i=1}^{N} \max(0,\ B_i - |p - X_i|)\)
That is, each radio tower \(i\) delivers a signal strength of \(\max(0,\ B_i - |p - X_i|)\) to coordinate \(p\). The signal weakens as one moves farther from the tower, and it no longer reaches once the distance is at least \(B_i\).
也就是说,每座无线电塔 \(i\) 在坐标 \(p\) 处提供的信号强度为 \(\max(0,\ B_i - |p - X_i|)\)。距离塔越远,信号越弱,一旦距离至少为 \(B_i\),信号就不再到达。
Takahashi's colleague Aoki will conduct signal strength surveys for \(Q\) intervals. For each survey \(j\), an interval \([L_j, R_j]\) is given. Find the maximum value of \(f(p)\) over all integer coordinates \(p\) within that interval.
高桥的同事青木将对 \(Q\) 个区间进行信号强度调查。对于每次调查 \(j\),给定一个区间 \([L_j, R_j]\)。求该区间内所有整数坐标 \(p\) 中 \(f(p)\) 的最大值。
【输入】
\(N\) \(Q\)
\(X_1\) \(B_1\)
\(X_2\) \(B_2\)
:
\(X_N\) \(B_N\)
\(L_1\) \(R_1\)
\(L_2\) \(R_2\)
:
\(L_Q\) \(R_Q\)
- The first line contains \(N\), the number of radio towers, and \(Q\), the number of surveys, separated by a space.
- The following \(N\) lines give information about each radio tower.
- The \((1 + i)\)-th line contains the coordinate \(X_i\) and the power output \(B_i\) of the \(i\)-th radio tower, separated by a space.
- The following \(Q\) lines give information about each survey.
- The \((1 + N + j)\)-th line contains the left endpoint \(L_j\) and the right endpoint \(R_j\) of the interval for the \(j\)-th survey, separated by a space.
【输出】
Output \(Q\) lines. The \(j\)-th line should contain the answer to the \(j\)-th survey, that is, the maximum value of \(f(p)\) over all integer coordinates \(p\) within the interval \([L_j, R_j]\).
【输入样例】
1 2
5 3
0 10
0 3
【输出样例】
3
1
【核心思想】
-
问题分析:给定 \(N\) 座无线电塔,每座塔位于坐标 \(X_i\),功率为 \(B_i\)。信号强度函数为 \(f(p) = \sum_{i=1}^{N} \max(0, B_i - |p - X_i|)\)。需要处理 \(Q\) 个查询,每个查询求区间 \([L_j, R_j]\) 内 \(f(p)\) 的最大值。这是一个差分 + 前缀和 + 线段树问题,关键在于将多个三角形信号叠加转化为单峰函数。
-
算法选择:
- 差分数组(Difference Array):记录每个信号源对斜率的贡献,将三角形信号转化为斜率变化
- 前缀和(Prefix Sum):通过两次前缀和(斜率→信号强度)计算每个位置的最终信号强度
- 线段树(Segment Tree):维护区间最大值,支持 \(O(\log N)\) 的区间最值查询
- 坐标偏移:使用
OFFSET = 200000处理负数坐标
-
关键步骤:
- 初始化:读取 \(N\)(信号源数量)、\(Q\)(查询次数)
- 差分标记信号源(处理 \(N\) 个信号源):
- 对于每个信号源 \((X_i, B_i)\),坐标偏移后 \(x = X_i + OFFSET\)
- 三角形信号在 \([x-B_i+1, x]\) 区间斜率为 \(+1\),在 \([x+1, x+B_i]\) 区间斜率为 \(-1\)
- 差分标记:
diff[x-B_i+1] += 1,diff[x+1] -= 2,diff[x+B_i+1] += 1
- 前缀和计算信号强度:
- 遍历 \(i\) 从 \(0\) 到 \(400000\):
curSlope += diff[i](当前斜率)curVal += curSlope(当前信号强度,斜率的积分)w[i] = curVal(存储每个位置的信号强度)
- 遍历 \(i\) 从 \(0\) 到 \(400000\):
- 构建线段树:基于
w数组构建,维护区间最大值 - 处理查询(处理 \(Q\) 个查询):
- 对于每个查询 \([L_j, R_j]\),坐标偏移后查询线段树区间最大值
-
时间/空间复杂度:
- 时间复杂度:\(O(N + Q \log M)\),其中 \(M = 400000\) 为坐标范围。差分标记 \(O(N)\),前缀和 \(O(M)\),每次查询 \(O(\log M)\)
- 空间复杂度:\(O(M)\),差分数组、信号强度数组和线段树
-
差分 + 线段树的核心思想:
- 三角形信号叠加:每个信号源的贡献是一个以 \(X_i\) 为中心、宽度为 \(2B_i\) 的三角形,通过差分记录斜率变化
- 两次前缀和:第一次前缀和得到斜率,第二次(积分)得到信号强度,将 \(O(N \times M)\) 的暴力计算优化为 \(O(N + M)\)
- 单峰函数性质:多个三角形叠加后,\(f(p)\) 是一个单峰(或分段单峰)函数,可用线段树维护最值
- 离线处理:先计算所有位置的信号强度,再统一回答查询
- 适用于区间覆盖、信号叠加、区间最值查询类问题
【算法标签】
线段树
【代码详解】
#include <bits/stdc++.h>
using namespace std;
#define int long long // 定义int为long long类型
const int N = 400005, INF = -1e18, OFFSET = 200000; // 常量定义
int n, q; // n: 信号源数量, q: 查询次数
int w[N]; // 最终信号强度数组
int diff[N]; // 差分数组,用于计算斜率变化
// 线段树节点结构体
struct Node
{
int l, r; // 节点覆盖的区间[l, r]
int dt, mx; // dt: 懒标记, mx: 区间最大值
}tr[N * 4]; // 线段树数组
// 向上更新:根据子节点更新父节点
void pushup(int u)
{
tr[u].mx = max(tr[u << 1].mx, tr[u << 1 | 1].mx); // 取左右子节点的最大值
}
// 向下传递懒标记
void pushdown(int u)
{
auto &root = tr[u], &l = tr[u << 1], &r = tr[u << 1 | 1]; // 引用当前节点和左右子节点
// 将懒标记传递给子节点
l.dt += root.dt, l.mx += root.dt;
r.dt += root.dt, r.mx += root.dt;
// 清空当前节点的懒标记
root.dt = 0;
}
// 构建线段树
void build(int u, int l, int r)
{
if (l == r) // 如果是叶子节点
{
tr[u] = {l, r, 0, w[l]}; // 初始化叶子节点
}
else
{
tr[u] = {l, r}; // 初始化非叶子节点
int mid = (l + r) >> 1; // 计算中点
// 递归构建左右子树
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
// 向上更新当前节点
pushup(u);
}
}
// 区间查询
int query(int u, int l, int r)
{
if (tr[u].l >= l && tr[u].r <= r) // 如果当前节点完全包含在查询区间内
{
return tr[u].mx; // 直接返回节点值
}
else
{
pushdown(u); // 下传懒标记
int mid = (tr[u].l + tr[u].r) >> 1; // 计算中点
int res = INF; // 初始化结果为负无穷
if (l <= mid) // 如果查询区间与左子树有交集
{
res = query(u << 1, l, r);
}
if (r > mid) // 如果查询区间与右子树有交集
{
res = max(res, query(u << 1 | 1, l, r)); // 取最大值
}
return res;
}
}
signed main()
{
cin >> n >> q; // 输入信号源数量和查询次数
// 处理每个信号源
for (int i = 1; i <= n; i++)
{
int x, b; // x: 中心位置, b: 半宽度
cin >> x >> b;
x += OFFSET; // 坐标偏移,使负数坐标变为正数
// 使用差分数组记录斜率变化
if (x - b + 1 >= 0) // 左边界
{
diff[x - b + 1] += 1; // 斜率+1
}
if (x + 1 <= 400000) // 中心点
{
diff[x + 1] -= 2; // 斜率-2
}
if (x + b + 1 <= 400000) // 右边界
{
diff[x + b + 1] += 1; // 斜率+1
}
}
// 计算最终的信号强度
int curSlope = 0; // 当前斜率
int curVal = 0; // 当前信号强度
for (int i = 0; i <= 400000; i++) // 遍历所有位置
{
curSlope += diff[i]; // 更新斜率
curVal += curSlope; // 通过积分得到信号强度
w[i] = curVal; // 存储信号强度
}
// 构建线段树
build(1, 0, 400000);
// 处理每个查询
while (q--)
{
int l, r; // 查询区间
cin >> l >> r;
l += OFFSET; // 坐标偏移
r += OFFSET;
cout << query(1, l, r) << endl; // 查询区间最大值
}
return 0; // 程序正常结束
}
【运行结果】
1 2
5 3
0 10
3
0 3
1
浙公网安备 33010602011771号