P2048 [NOI2010] 超级钢琴 题解

\(Description\)

Luogu传送门

\(Solution\)

早就想做这道 P2048 了,但是之前太菜了,不会 QwQ

RMQ + 前缀和 + 堆

显然要做一个前缀和,但是做完前缀和之后暴力枚举区间显然是不行的。

考虑一下我们到底要计算什么:

对于一个点 \(u\),我们要计算 \(\max\{sum_t - sum_{u - 1}\}\ (\ t \in [u + L - 1, u + R - 1])\)

\(sum_{u - 1}\) 是固定的,所以我们要使用某种方法快速计算出 \(\max\{sum_t\}\ \ (t \in [u + L - 1, u + R - 1])\)

这时候 \(RMQ\) 就闪亮登场了。

关于 \(RMQ\) 不会的话去看其他 dalao 的博客吧=-=

简单来说就是开个数组 \(f_{i, k}\) 表示从 \(i\) 开始长度为 \(2^k\) 的区间中的(最大值/最小值)。

然后预处理一下这个数组,查询的时候就可以 \(O(1)\) 查询了。

然后类似于 异或粽子 那题,开个大根堆,每次取出当前堆顶,插入次大的值。

重复上述操作即可。

\(Code\)

#include <bits/stdc++.h>
#define ll long long

using namespace std;

namespace IO{
    inline int read(){
        int x = 0, f = 1;
        char ch = getchar();
        while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
        while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
        return x * f;
    }

    template <typename T> inline void write(T x){
        if(x < 0) putchar('-'), x = -x;
        if(x > 9) write(x / 10);
        putchar(x % 10 + '0');
    }
}
using namespace IO;

const int N = 5e5 + 10;
int n, k, L, R;
int f[N][20];
ll sum[N];

inline void init(){
    for(int i = 1; i <= n; ++i) f[i][0] = i;
    for(int j = 1; (1 << j) <= n; ++j)
        for(int i = 1; i + (1 << j) - 1 <= n; ++i){
            int x = f[i][j - 1], y = f[i + (1 << (j - 1))][j - 1];
            f[i][j] = sum[x] > sum[y] ? x : y;
        }
}

inline int query(int l, int r){
    int k = log2(r - l + 1);
    int x = f[l][k], y = f[r - (1 << k) + 1][k];
    return sum[x] > sum[y] ? x : y;
}

struct node{
    int u, l, r, t;
    node(int _u = 0, int _l = 0, int _r = 0){
        u = _u, l = _l, r = _r, t = query(_l, _r);
    }

    inline bool operator < (const node &b) const{
        return sum[t] - sum[u - 1] < sum[b.t] - sum[b.u - 1];
    }
};
priority_queue <node> q;

int main(){
    n = read(), k = read(), L = read(), R = read();
    for(int i = 1; i <= n; ++i) sum[i] = sum[i - 1] + read();
    init();
    for(int i = 1; i <= n; ++i)
        if(i + L - 1 <= n) q.push(node{i, i + L - 1, min(i + R - 1, n)});
    ll ans = 0;
    while(k--){
        node now = q.top(); q.pop();
        ans += sum[now.t] - sum[now.u - 1];
        if(now.t != now.l) q.push(node{now.u, now.l, now.t - 1});
        if(now.t != now.r) q.push(node{now.u, now.t + 1, now.r});
    }
    write(ans), puts("");
    return 0;
}

\[\_EOF\_ \]

posted @ 2021-12-29 18:10  xixike  阅读(62)  评论(0编辑  收藏  举报