1. 程式人生 > >[洛谷P2048] [NOI2010] 超級鋼琴

[洛谷P2048] [NOI2010] 超級鋼琴

new 通過 技術分享 音樂 main getchar 正整數 sequence getc

洛谷題目鏈接:[NOI2010]超級鋼琴

題目描述

小Z是一個小有名氣的鋼琴家,最近C博士送給了小Z一架超級鋼琴,小Z希望能夠用這架鋼琴創作出世界上最美妙的音樂。

這架超級鋼琴可以彈奏出n個音符,編號為1至n。第i個音符的美妙度為Ai,其中Ai可正可負。

一個“超級和弦”由若幹個編號連續的音符組成,包含的音符個數不少於L且不多於R。我們定義超級和弦的美妙度為其包含的所有音符的美妙度之和。兩個超級和弦被認為是相同的,當且僅當這兩個超級和弦所包含的音符集合是相同的。

小Z決定創作一首由k個超級和弦組成的樂曲,為了使得樂曲更加動聽,小Z要求該樂曲由k個不同的超級和弦組成。我們定義一首樂曲的美妙度為其所包含的所有超級和弦的美妙度之和。小Z想知道他能夠創作出來的樂曲美妙度最大值是多少。

輸入輸出格式

輸入格式:

輸入第一行包含四個正整數n, k, L, R。其中n為音符的個數,k為樂曲所包含的超級和弦個數,L和R分別是超級和弦所包含音符個數的下限和上限。

接下來n行,每行包含一個整數Ai,表示按編號從小到大每個音符的美妙度。

輸出格式:

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

輸入輸出樣例

輸入樣例#1:

4 3 2 3
3
2
-6
8

輸出樣例#1:

11

說明

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

1.    音符1 ~ 2,美妙度為3 + 2 = 5
2.    音符2 ~ 3,美妙度為2 + (-6) = -4
3.    音符3 ~ 4,美妙度為(-6) + 8 = 2
4.    音符1 ~ 3,美妙度為3 + 2 + (-6) = -1
5.    音符2 ~ 4,美妙度為2 + (-6) + 8 = 4

最優方案為:樂曲由和弦1,和弦3,和弦5組成,美妙度為5 + 2 + 4 = 11。

技術分享圖片

所有數據滿足:-1000 ≤ Ai ≤ 1000,1 ≤ L ≤ R ≤ n且保證一定存在滿足要求的樂曲。

一句話題意: 在一個長度為\(n\)的序列中,要求出其中的最大的\(k\)段不同的子序列,並且滿足這個子序列的長度∈\([l,r]\).

題解: 既然限定了區間的長度,所以可以考慮枚舉區間的左端點,那麽這樣合法的右端點就組成了一個區間,對於同一個左端點,它的合法右端點的前綴和的最大值顯然是這個左端點能取得的最大值,並且合法右端點的區間的第2大,第3大減去左端點的前綴和也一定是單調遞減的.這讓我們聯想到[洛谷P1631] 序列合並.

通過枚舉左端點,在合法右端點中查找第1大的前綴和,然後將它減去左端點的前綴和,再將這個值加入堆中,這樣每次取出堆中的最大值,加入答案,這樣堆維護的事實上就是一個區間.它具有左端點,並且記錄了查找到第幾大,以這個區間的權值作為鍵值維護堆.

然後每次取出堆中的最大值,也就是取出了這個區間,然後就可以將這個左端點對應的合法右端點的前綴和的下一個最大值加入堆中,這樣維護出的前\(k\)大一定是最優的.

靜態區間第\(k\)大可以用主席樹實現,其他的參數註意不要打錯,要開long long.

#include<bits/stdc++.h>
using namespace std;
const int N=500000+5;
typedef int _int;
#define int long long

int n, k, l, r, a[N], s[N], rk[N], root[N], temp[N], vis[N], size, cnt = 0, ans = 0;

struct president_tree{
    int ls, rs, cnt;
}t[N*20];

struct number{
    int id1, id2, val;
    bool operator < (const number &a) const{
        return val < a.val;
    }
};

priority_queue <number> h;

inline int gi(){
    int ans = 0, f = 1; char i = getchar();
    while(i<'0' || i>'9'){ if(i == '-') f = -1; i = getchar(); }
    while(i>='0' && i<='9') ans = ans*10+i-'0', i = getchar();
    return ans * f;
}

inline void update(int &x, int last, int pos, int l = 1, int r = size){
    x = ++cnt; t[x] = t[last]; t[x].cnt++;
    if(l == r) return; int mid = (l+r>>1);
    if(pos <= mid) update(t[x].ls, t[last].ls, pos, l, mid);
    else update(t[x].rs, t[last].rs, pos, mid+1, r);
}

inline int query(int x, int last, int k, int l = 1, int r = size){
    if(l == r) return vis[l];
    int mid = (l+r>>1), sum = t[t[x].ls].cnt-t[t[last].ls].cnt;
    if(k <= sum) return query(t[x].ls, t[last].ls, k, l, mid);
    return query(t[x].rs, t[last].rs, k-sum, mid+1, r);
}

_int main(){
    // freopen("sequence.in", "r", stdin);
    // freopen("sequence.out", "w", stdout);
    n = gi(), k = gi(), l = gi(), r = gi();
    for(int i=1;i<=n;i++) a[i] = gi(), s[i] = s[i-1]+a[i];
    memcpy(temp, s, sizeof(s));
    sort(temp+1, temp+n+1); size = unique(temp+1, temp+n+1)-temp-1;
    for(int i=1;i<=n;i++){
        rk[i] = lower_bound(temp+1, temp+size+1, s[i])-temp;
        vis[rk[i]] = s[i];
    }
    for(int i=1;i<=n;i++) update(root[i], root[i-1], rk[i]);
    for(int tmp, i=1;i<=n-l+1;i++){
        int lim = min(n, i+r-1);
        h.push((number){ i, lim-i-l+2, query(root[lim], root[i+l-2], lim-i-l+2)-s[i-1] });
    }
    for(int i=1;i<=k;i++){
        number top, tmp; top = h.top(); h.pop();
        ans += top.val;
        if(top.id2 <= 1) continue;
        tmp.id2 = top.id2-1, tmp.id1 = top.id1;
        int lim = min(n, top.id1+r-1);
        tmp.val = query(root[lim], root[tmp.id1+l-2], tmp.id2)-s[top.id1-1];
        h.push(tmp);
    }
    cout << ans << endl;
    return 0;
}

[洛谷P2048] [NOI2010] 超級鋼琴