发射站

GitHub跳转原题关系图返回列表

分别用单调栈求每个发射站左右最近更高站,再把能量累加到对应接收站上。

OJ: luogu

题目 ID: P1901

难度:普及/提高-

标签:单调栈noip

日期: 2026-06-18 16:24

题意

给出 n 个排成一行的发射站,每个站有互不相同的高度 h 和能量 v

每个站的能量会分别传给左右两边“最近的更高站”。

要求统计所有站接收到的总能量,输出其中最大值。

思路

先看最直接的做法:对每个站分别往左、往右暴力找最近更高站。

这个版本最容易理解:

cpp
#include <bits/stdc++.h>
using namespace std;

// brute.cpp:小数据暴力解,用来帮助理解题意并辅助对拍。
// 对每个发射站直接向左右两边暴力寻找最近的更高站。
const int MAXN = 1000000 + 5;

int n;
long long h[MAXN], v[MAXN];
long long rec[MAXN]; // rec[i] 表示第 i 个发射站收到的总能量

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> h[i] >> v[i];
    }

    for (int i = 1; i <= n; i++) {
        // 向左暴力找最近的更高站。
        int j = i - 1;
        while (j >= 1 && h[j] < h[i]) {
            j--;
        }
        if (j >= 1) {
            rec[j] += v[i];
        }

        // 向右暴力找最近的更高站。
        j = i + 1;
        while (j <= n && h[j] < h[i]) {
            j++;
        }
        if (j <= n) {
            rec[j] += v[i];
        }
    }

    long long ans = 0;
    for (int i = 1; i <= n; i++) {
        ans = max(ans, rec[i]);
    }

    cout << ans << '\n';
    return 0;
}

它要对每个站都向左右暴力扫描,最坏会退化到 O(n2)O(n^2),显然不能处理 n=10^6

注意“最近更高站”就是单调栈的经典应用。

左边最近更高站

从左到右扫描,栈里维护高度严格递减的站编号。

处理站 i 时:

  1. 先把栈顶所有高度小于 h[i] 的站弹掉;
  2. 如果栈不空,栈顶就是 i 左边最近的更高站;
  3. i 入栈。

右边最近更高站

再从右到左做一遍完全对称的扫描,就能找到右边最近更高站。

这样每个站只会进栈、出栈各一次。两次扫描结束后,把每个站的能量加到左右两个接收站上,最后在所有接收值中取最大值即可。

代码

cpp
#include <bits/stdc++.h>
using namespace std;

const int MAXN = 1000000 + 5;

int n;
long long h[MAXN], v[MAXN];
long long rec[MAXN]; // rec[i] 表示第 i 个发射站最终收到的总能量
int st[MAXN];        // 手写栈,保存可能成为“最近更高站”的位置
int top_pos;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> h[i] >> v[i];
    }

    // 从左到右,求每个点左边最近的更高站。
    top_pos = 0;
    for (int i = 1; i <= n; i++) {
        while (top_pos > 0 && h[st[top_pos]] < h[i]) {
            top_pos--;
        }
        if (top_pos > 0) {
            rec[st[top_pos]] += v[i];
        }
        st[++top_pos] = i;
    }

    // 从右到左,求每个点右边最近的更高站。
    top_pos = 0;
    for (int i = n; i >= 1; i--) {
        while (top_pos > 0 && h[st[top_pos]] < h[i]) {
            top_pos--;
        }
        if (top_pos > 0) {
            rec[st[top_pos]] += v[i];
        }
        st[++top_pos] = i;
    }

    long long ans = 0;
    for (int i = 1; i <= n; i++) {
        ans = max(ans, rec[i]);
    }

    cout << ans << '\n';
    return 0;
}

复杂度

  • 时间复杂度:O(n)O(n)
  • 空间复杂度:O(n)O(n)

总结

这题的关键是把“左右最近更高站”识别成单调栈模型。

每个站只会被弹出一次,所以整套流程是线性的。

一图流解析

这张图把本题的建模、关键转移、实现检查和训练方法压缩到一页,适合读完正文后复盘。

一图流解析