P2048 超級鋼琴(RMQ+優先隊列)

題目描述

小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且保證一定存在滿足要求的樂曲。

解題思路:

第一種做法是先求出前綴和。

用ST表預處理出從[l,r]的前綴和最大值。

然後有一個結構體,struct{ pos, l, r, val},各個元素的意思是:從pos這個位置到[l,r]這個區間的各個區間的和最大,這個val可以根據ST表求出來,把這些值先放進優先隊列,爲val重載小於運算符。然後不斷從優先隊列中取出大的值,再將取完後的結構體分解成兩個結構體存到優先隊列中。

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

using namespace std;
const int maxn = 5e5+5;

int st[maxn][20];
int a[maxn], sum[maxn];

void init(int n)
{
    int N = floor(log2(n));
    for(int j = 1; j <= N; j++)
    {
        for(int i = 1; i <= n+1-(1<<j); i++)
        {
            if(sum[st[i][j-1]]>sum[st[i+(1<<(j-1))][j-1]])
                st[i][j] = st[i][j-1];
            else
                st[i][j] = st[i+(1<<(j-1))][j-1];
        }
    }
}
int get(int l, int r)
{
    int k = floor(log2(r+1-l));
    if(sum[st[l][k]]>sum[st[r+1-(1<<k)][k]])
        return st[l][k];
    else
        return st[r+1-(1<<k)][k];
}

struct node{
    int pos, l, r,pp,val;
    int operator <(const node &rhs) const{
        return val < rhs.val;
    }
};
priority_queue<node>q;

int main()
{
    int n, k, l, r;
    scanf("%d%d%d%d", &n, &k, &l, &r);
    l--;r--;
    for(int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
        sum[i] = sum[i-1]+a[i];
        st[i][0] = i;
    }
    init(n);
//    cout << sum[get(3,4)]-sum[0]<<endl;
    int pos;
    for(int i = 1; i+l <= n; i++)
    {
        int ll = i+l;
        int rr = min(i+r,n);
        pos = get(ll,rr);
        q.push(node{i,ll,rr,pos,sum[pos]-sum[i-1]});
    }
    long long ans = 0;
    while(k--)
    {
        node no =q.top();
        ans+=no.val;
        q.pop();
        if(no.pp-1>=no.l){
            pos = get(no.l,no.pp-1);
            q.push(node{no.pos,no.l,no.pp-1,pos,sum[pos]-sum[no.pos-1]});
        }
        if(no.pp+1<=no.r)
        {
            pos = get(no.pp+1,no.r);
            q.push(node{no.pos,no.pp+1,no.r,pos,sum[pos]-sum[no.pos-1]});
        }
    }
    printf("%lld\n", ans);
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章