洛谷 P2048 [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 ~ 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。 所有資料滿足:-1000 ≤ Ai ≤ 1000,1 ≤ L ≤ R ≤ n且保證一定存在滿足要求的樂曲。
分析: 我們可以先跑一個字首和,考慮每一個位置開始的,長度為的串的最大值。 一個串可以表示成,起點一定,顯然相當於求最大值,然後丟進一個堆裡。 當我們選了一個堆頂串,並統計了答案後,假設這個串是以開頭的第大的,我們把這個東西從以開頭的第大的串插入,如果沒有就不插入。第大直接維護主席樹即可。
程式碼:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <algorithm>
#define LL long long
const int maxn=5e5+7;
using namespace std;
int n,m,l,r,cnt;
int root[maxn],b[maxn],a[maxn];
LL ans;
struct node{
int l,r,data,sum;
}t[maxn*25];
struct rec{
int x,st,k;
};
bool operator <(rec a,rec b)
{
return a.x<b.x;
}
priority_queue <rec> q;
void ins(int &p,int q,int l,int r,int x)
{
if (!p) p=++cnt;
t[p].data=t[q].data+1;
if (l==r) return;
int mid=(l+r)/2;
if (x<=mid) t[p].r=t[q].r,ins(t[p].l,t[q].l,l,mid,x);
else t[p].l=t[q].l,ins(t[p].r,t[q].r,mid+1,r,x);
}
int getrank(int p,int q,int l,int r,int k)
{
if (l==r) return l;
int mid=(l+r)/2;
int sum=t[t[p].r].data-t[t[q].r].data;
if (sum>=k) return getrank(t[p].r,t[q].r,mid+1,r,k);
else return getrank(t[p].l,t[q].l,l,mid,k-sum);
}
int main()
{
scanf("%d%d%d%d",&n,&m,&l,&r);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
for (int i=1;i<=n;i++)
{
a[i]+=a[i-1];
b[i]=a[i];
}
sort(b+1,b+n+1);
int size=unique(b+1,b+n+1)-b-1;
for (int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+size+1,a[i])-b;
for (int i=1;i<=n;i++) ins(root[i],root[i-1],1,n,a[i]);
for (int i=1;i<=n;i++)
{
int L=i+l-1,R=min(i+r-1,n);
if (L>n) continue;
int x=b[getrank(root[R],root[L-1],1,n,1)]-b[a[i-1]];
q.push((rec){x,i,1});
}
for (int i=1;i<=m;i++)
{
rec c=q.top();
q.pop();
ans+=(LL)c.x;
int L=c.st+l-1,R=min(c.st+r-1,n);
if ((L<=n) && (R-L+1>c.k))
{
int x=b[getrank(root[R],root[L-1],1,n,c.k+1)]-b[a[c.st-1]];
q.push((rec){x,c.st,c.k+1});
}
}
printf("%lld",ans);
}