题解: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

【核心思想】

  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)\) 的最大值。这是一个差分 + 前缀和 + 线段树问题,关键在于将多个三角形信号叠加转化为单峰函数。

  2. 算法选择

    • 差分数组(Difference Array):记录每个信号源对斜率的贡献,将三角形信号转化为斜率变化
    • 前缀和(Prefix Sum):通过两次前缀和(斜率→信号强度)计算每个位置的最终信号强度
    • 线段树(Segment Tree):维护区间最大值,支持 \(O(\log N)\) 的区间最值查询
    • 坐标偏移:使用 OFFSET = 200000 处理负数坐标
  3. 关键步骤

    • 初始化:读取 \(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] += 1diff[x+1] -= 2diff[x+B_i+1] += 1
    • 前缀和计算信号强度
      • 遍历 \(i\)\(0\)\(400000\)
        • curSlope += diff[i](当前斜率)
        • curVal += curSlope(当前信号强度,斜率的积分)
        • w[i] = curVal(存储每个位置的信号强度)
    • 构建线段树:基于 w 数组构建,维护区间最大值
    • 处理查询(处理 \(Q\) 个查询):
      • 对于每个查询 \([L_j, R_j]\),坐标偏移后查询线段树区间最大值
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N + Q \log M)\),其中 \(M = 400000\) 为坐标范围。差分标记 \(O(N)\),前缀和 \(O(M)\),每次查询 \(O(\log M)\)
    • 空间复杂度:\(O(M)\),差分数组、信号强度数组和线段树
  5. 差分 + 线段树的核心思想

    • 三角形信号叠加:每个信号源的贡献是一个以 \(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
posted @ 2026-06-15 09:50  团爸讲算法  阅读(9)  评论(0)    收藏  举报