bzoj2006 [NOI2010]超級鋼琴 [優先隊列|RMQ]

Description

小Z是一個小有名氣的鋼琴家,最近C博士送給了小Z一架超級鋼琴,小Z希望能夠用這架鋼琴創作出世界上最美妙的音樂。 這架超級鋼琴可以彈奏出n個音符,編號爲1至n。第i個音符的美妙度爲Ai,其中Ai可正可負。 一個“超級和絃”由若干個編號連續的音符組成,包含的音符個數不少於L且不多於R。我們定義超級和絃的美妙度爲其包含的所有音符的美妙度之和。兩個超級和絃被認爲是相同的,當且僅當這兩個超級和絃所包含的音符集合是相同的。 小Z決定創作一首由k個超級和絃組成的樂曲,爲了使得樂曲更加動聽,小Z要求該樂曲由k個不同的超級和絃組成。我們定義一首樂曲的美妙度爲其所包含的所有超級和絃的美妙度之和。小Z想知道他能夠創作出來的樂曲美妙度最大值是多少。

Input

第一行包含四個正整數n, k, L, R。其中n爲音符的個數,k爲樂曲所包含的超級和絃個數,L和R分別是超級和絃所包含音符個數的下限和上限。 接下來n行,每行包含一個整數Ai,表示按編號從小到大每個音符的美妙度。

Output

只有一個整數,表示樂曲美妙度的最大值。

Sample Input

4 3 2 3
3
2
-6
8

Sample Output

11

Hint

共有5種不同的超級和絃:

音符1 ~ 2,美妙度爲3 + 2 = 5
音符2 ~ 3,美妙度爲2 + (-6) = -4
音符3 ~ 4,美妙度爲(-6) + 8 = 2
音符1 ~ 3,美妙度爲3 + 2 + (-6) = -1
音符2 ~ 4,美妙度爲2 + (-6) + 8 = 4
最優方案爲:樂曲由和絃1,和絃3,和絃5組成,美妙度爲5 + 2 + 4 = 11。

Solution

先解釋一下題目中的“兩個超級和絃被認爲是相同的,當且僅當這兩個超級和絃所包含的音符集合是相同的”
這句話就是說,同一個區間[l, r]不能用多次。假設區間[l1, r1]所包含的元素與[l, r]包括順序完全相同,這兩個區間也是可以同時使用的。
唉就是這句話把我坑了啊……我一開始還想怎麼判斷兩個區間包含的元素相同……結果發現傻逼了……

好了言歸正傳,說下本題思路:
以開始位置爲i的區間,它的結束位置只能是[i+L1,i+R1] 之間。
假設我們要求以i開始的區間,它的最大得分是多少,即sum[j]sum[i1] 最大,很顯然只要RMQ(i+L1,i+R1) 求出最大的sum[j] 即可。
接下來開始本題的關鍵:

  • 設開始位置爲i,結束位置爲[l,r],最大得分點爲j,最大得分爲v,將i,l,r,j,v存入一個節點。
  • 枚舉i=1 to n,將所有節點放進一個優先隊列。那麼此時隊首的元素就是得分最大的區間。
  • 依次將隊首元素取出,此時對於點i來說,點j已經不能用了。原區間分裂成[l,j1] ,[j+1,r] 兩個區間,我們再用RMQ分別算出新區間的v和j,將其加入優先隊列。
  • 重複以上步驟直到取出了k個元素。

最後再膜拜一下手寫堆的巨巨orz……

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

using namespace std;

const int MAXN = 500005;

int n, k, minlen, maxlen;
long long arr[MAXN];
long long sum[MAXN];

int ST[MAXN][20];
int IDX[MAXN][20];
void initST() {
    for (int i = 1; i <= n; i++) {
        ST[i][0] = sum[i];
        IDX[i][0] = i;
    }
    for (int j = 1; j <= 20; j++)
        for (int i = 1; i <= n; i++)
            if (i + (1 << j) - 1 <= n) {
                if (ST[i][j - 1] > ST[i + (1 << j - 1)][j - 1]) {
                    ST[i][j] = ST[i][j - 1];
                    IDX[i][j] = IDX[i][j - 1];
                } else {
                    ST[i][j] = ST[i + (1 << j - 1)][j - 1];
                    IDX[i][j] = IDX[i + (1 << j - 1)][j - 1];
                }
            }
}
int query(int a, int b) {
    int k = log(b - a + 1) / log(2);
    return max(ST[a][k], ST[b - (1 << k) + 1][k]);
}
int query_idx(int a, int b) {
    int k = log(b - a + 1) / log(2);
    if (ST[a][k] > ST[b - (1 << k) + 1][k]) return IDX[a][k];
    else return IDX[b - (1 << k) + 1][k];
}

struct Node{
    int i, idx, l, r, v;
    Node() {}
    Node(int a, int b, int c, int d, int e)
        : i(a), idx(b), l(c), r(d), v(e) {}
    bool operator < (const Node &n) const {
        return v < n.v;
    }
};

priority_queue<Node> q;

int main() {
    scanf("%d %d %d %d", &n, &k, &minlen, &maxlen);
    for (int i = 1; i <= n; i++) {
        scanf("%lld", &arr[i]);
        sum[i] = sum[i - 1] + arr[i];
    }
    initST();
    for (int i = 1; i + minlen - 1 <= n; i++) {
        int l = i + minlen - 1;
        int r = min(n, i + maxlen - 1);
        int v = query(l, r);
        int idx = query_idx(l, r);
        q.push(Node(i, idx, l, r, v - sum[i - 1]));
    }
    long long ans = 0;
    while (k--) {
        Node cur = q.top();
        q.pop();
        ans += cur.v;
        if (cur.idx > cur.l) {
            int l = cur.l;
            int r = cur.idx - 1;
            int v = query(l, r);
            int idx = query_idx(l, r);
            q.push(Node(cur.i, idx, l, r, v - sum[cur.i - 1]));
        }
        if (cur.idx < cur.r) {
            int l = cur.idx + 1;
            int r = cur.r;
            int v = query(l, r);
            int idx = query_idx(l, r);
            q.push(Node(cur.i, idx, l, r, v - sum[cur.i - 1]));
        }
    }
    printf("%lld\n", ans);
    return 0;
}
發佈了194 篇原創文章 · 獲贊 9 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章