BZOJ2006 [NOI2010] ST表 + 堆

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

[樣例說明]

共有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。

HINT

N <= 500,000,k <= 500,000
-1000 <= Ai <= 1000, 1 <= L <= R<= N且保證一定存在滿足條件的樂曲


Solution :

    這道題需要找到子區間第 K 大的值,那我們先考慮如何給定一個端點去找最大的區間,首先算區間和很容易前綴,然後是給定區間大小尋找區間,因爲一個端點是定的,這裏我定的是左端點, 我們只需要去尋找右區間的前綴的最大值,從公式是很容易看出dismax=max(pre[i]pre[now])now+L1inow+R1 找最大值的話,你可以線段樹什麼花式找都可以,但畢竟只是要一個最大值,爲什麼不用ST表呢,簡單好些,O(1) 查詢,於是我們就有了找到最大值的用法。
    現在考慮找到第 K 大的用法,我們可以用個堆維護信息,每次維護 now , ls , rs , pos , val 五個信息,now 表示該狀態的左端點,lsrs 表示該狀態在 lsrs 的區間尋找最大值, pos 表示在 pos 位置找到了最大值, val 表示該狀態的最大值。當我們已經記錄了該狀態的答案,我們就不需要 pos 的信息, 也就是爲了不再使用 pos 的信息,我們把狀態分成 (now,ls,pos1,pos,val)(now,pos+1,rs,pos′′,val′′) ,這樣就避開了 pos 。然後就愉快的做出來了。



Code :

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <ctime>
#include <map>
#include <vector>
#include <queue>
#define LL long long 
using namespace std;

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

const int MAXN = 5e5 + 5;
int a[MAXN], pre[MAXN], n, k, Log[MAXN], mx[20][MAXN];
LL ans;

struct point {
    int l, r, i, val, pos;
    point() {}
    point(int _l, int _r, int _i, int _val, int _pos) : l(_l), r(_r), i(_i), val(_val), pos(_pos) {}
    bool operator < (const point & a) const {
        return val < a.val;
    }
};

inline int new_max(int x, int y) {
    return pre[x] > pre[y] ? x : y;
}

inline void ST() {
    Log[0] = -1;
    for(int i = 1; i <= n; ++i) Log[i] = Log[i >> 1] + 1;
    for(int i = 1; i <= n; ++i) mx[0][i] = i;
    for(int j = 1; (1 << j) <= n; ++j)
        for(int i = 1; i + (1 << j) - 1 <= n; ++i)
            mx[j][i] = new_max(mx[j - 1][i], mx[j - 1][i + (1 << j - 1)]);
}

inline int find(int l, int r) {
    int k = Log[r - l + 1];
    return new_max(mx[k][l], mx[k][r - (1 << k) + 1]);
}

priority_queue<point> q;

inline void wr(LL x){
    if(x < 0) putchar('-'), x = -x;
    if(x > 9) wr(x / 10);
    putchar(x % 10 + '0');
}

int main() {
    n = read(), k = read(); int L = read(), R = read();
    for(int i = 1; i <= n; ++i) a[i] = read(), pre[i] = pre[i - 1] + a[i];
    ST();
    for(int i = 1; i <= n; ++i)
        if(i + L - 1 <= n) {
            int ls = i + L - 1, rs = min(i + R - 1, n);
            int pos = find(ls, rs), val = pre[pos] - pre[i - 1];
            q.push(point(ls, rs, i, val, pos));
        }
        else break;

    while(!q.empty() && k) {
        --k;
        point now = q.top(); q.pop();
        ans += now.val;
        point ls = now, rs = now;
        ls.r = now.pos - 1;
        if(ls.r >= ls.l)
            ls.pos = find(ls.l, ls.r), ls.val = pre[ls.pos] - pre[ls.i - 1], q.push(ls);
        rs.l = now.pos + 1;
        if(rs.r >= rs.l)
            rs.pos = find(rs.l, rs.r), rs.val = pre[rs.pos] - pre[rs.i - 1], q.push(rs);
    }
    wr(ans);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章