发射站(单调队列)

题意

某地有 (N) 个能量发射站排成一行,每个发射站 (i) 都有不相同的高度 (H_i),并能向两边(当然两端的只能向一边)同时发射能量值为 (V_i) 的能量,并且发出的能量只被两边最近的且比它高的发射站接收。

显然,每个发射站发来的能量有可能被 (0)(1)(2) 个其他发射站所接受,出于安全考虑,每个发射站接收到的能量总和是我们很关心的问题。

由于数据很多,现在只需要你帮忙计算出接收最多能量的发射站接收的能量是多少。

数据范围

(1 leq N leq 1000000)

思路

因为是向两边发射能量,因此我们可以考虑将两个方向分开考虑。因为两个方向等价,所以我们只考虑向左发射的情形。

遍历序列中的每一个发射站,考虑它能接收到谁发出的能量。

因为本题肯定要采取(O(n))复杂度的算法,因此先对过程进行分析,寻求有用的性质。

考虑相邻两个发射站(i, i + 1),如果(H_i < H_{i + 1}),则(i)号发射站就不能接收到之后的能量了。

根据这条单调性,可以考虑使用单调队列。如果当前发射站的高度大于队尾发射站的高度,就将队尾发射站出队。这样队列中维护的就是高度递减的发射站序列。

再考虑如何更新答案,队列中的第(i)个发射站,只能将能量传给(i-1)个发射站,因此每次入队的时候更新一下前一个的接收能量总和即可。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 1000010;

int n;
int h[N], v[N];
int sum[N];
int q[N];

int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i ++) scanf("%d%d", &h[i], &v[i]);
    int tt = 0, hh = 0;
    for(int i = 1; i <= n; i ++) {
        while(tt > hh && h[q[tt]] < h[i]) tt --;
        q[++ tt] = i;
        if(tt > 1) sum[q[tt - 1]] += v[q[tt]];
    }
    tt = 0; hh = 0;
    for(int i = n; i >= 1; i --) {
        while(tt > hh && h[q[tt]] < h[i]) tt --;
        q[++ tt] = i;
        if(tt > 1) sum[q[tt - 1]] += v[q[tt]];
    }
    int res = 0;
    for(int i = 1; i <= n; i ++) res = max(res, sum[i]);
    printf("%d
", res);
    return 0;
}
原文地址:https://www.cnblogs.com/miraclepbc/p/14459067.html