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

洛谷 P2048 [NOI2010]超級鋼琴 解題報告

最大值 相同 algo src n-n 可能 noi fine 輸入輸出格式

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,表示按編號從小到大每個音符的美妙度。

輸出格式:

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

說明

技術分享圖片

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


題意:求前\(k\)大長度在\(L\)~\(R\)之間的子序列之和

用堆維護\(ST\)表的做法沒想到。。

想了個暴力\(nlog^2n\)的做法

枚舉子序列左端點\(l\),則可能的右端點為一個長為\(R+1-L\)

的序列,我們發現對於可能的右端點\(r\),產生的答案為\(f[r]-f[l-1]\),則每一個左端點固定後減去的值是一定的,只是產生的右端點劃過去了。當然每個左端點可能產生多個子序列,我們不能只知道它的最大值。這時候,我們就可以想到拿主席樹維護前綴和數組的靜態區間第\(k\)值了,然後用一個堆維護每一個左端點所對應的區間取到第幾大了。

然而我主席樹一直寫的不熟調了好久。。


Code:

#include <cstdio>
#include <queue>
#include <algorithm>
#include <iostream>
#define ll long long
using namespace std;
const ll N=2000010;
ll n,k,l0,r0;
ll d[N],score[N],root[N],dat[N<<4],L[N<<4],R[N<<4],num[N],cnt;
pair <ll ,ll > f[N];
ll build(ll l,ll r)
{
    ll now=++cnt;
    if(l==r) return now;
    ll mid=l+r>>1;
    L[now]=build(l,mid);
    R[now]=build(mid+1,r);
    return now;
}
ll rebuild(ll last,ll l,ll r,ll pos)
{
    ll now=++cnt;
    if(l==r)
    {
        dat[now]=1;
        return now;
    }
    ll mid=l+r>>1;
    if(pos<=mid)
    {
        L[now]=rebuild(L[last],l,mid,pos);
        R[now]=R[last];
    }
    else
    {
        L[now]=L[last];
        R[now]=rebuild(R[last],mid+1,r,pos);
    }
    dat[now]=dat[L[now]]+dat[R[now]];
    return now;
}
ll query(ll last,ll now,ll l,ll r,ll num)
{
    if(l==r) return l;
    ll mid=l+r>>1;
    if(dat[L[now]]-dat[L[last]]>=num)
        return query(L[last],L[now],l,mid,num);
    else
        return query(R[last],R[now],mid+1,r,num-(dat[L[now]]-dat[L[last]]));
}
void init()
{
    scanf("%lld%lld%lld%lld",&n,&k,&l0,&r0);
    for(ll i=1;i<=n;i++)
    {
        scanf("%lld",&score[i]);
        score[i]+=score[i-1];
        f[i].second=i;
        f[i].first=score[i];
    }
    sort(f+1,f+1+n);
    for(ll i=1;i<=n;i++)
    {
        num[f[i].second]=n-i+1;
        d[n-i+1]=f[i].first;
    }
    root[0]=build(1,n);
    for(ll i=1;i<=n;i++)
        root[i]=rebuild(root[i-1],1,n,num[i]);
}
priority_queue <pair <ll,ll > > q;
pair <ll,ll > p;
ll top[N];
void work()
{
    for(ll i=l0;i<=n;i++)
    {
        ll r=min(i+r0-l0,n);
        p.first=d[query(root[i-1],root[r],1,n,++top[i])]-score[i-l0];
        p.second=i;
        q.push(p);
    }
    ll ans=0;
    while(k--)
    {
        ll now=q.top().second;
        ans+=q.top().first;
        q.pop();
        if(top[now]>=min(r0-l0,n-now)+1) continue;
        p.first=d[query(root[now-1],root[min(now+r0-l0,n)],1,n,++top[now])]-score[now-l0];
        p.second=now;
        q.push(p);
    }
    printf("%lld\n",ans);
}
int main()
{
    init();
    work();
    return 0;
}

2018.7.5

洛谷 P2048 [NOI2010]超級鋼琴 解題報告