洛谷P2048 [NOI2010]超级钢琴 (优先队列+RMQ)

题目链接

https://www.luogu.org/problem/P2048

题意

给出n个音符的美妙度,“超级和旋”由若干个编号连续的音符组成,包含的音符个数不少于L其不多于R,我们定义超级和弦的美妙度为其包含的所有音符的美妙度之和,求由k个超级和旋组成的乐曲的美妙度之和最大值是多少

思路

输出最大的k个sum[r]-sum[l-1] (L<=r-l+1<=R)之和
当右端点固定不变时,左端点的前缀和越小越好
固定右端点r后,左端点的被限制在了区间[r-R,r-L]内
RMQ查出在这段左端点区间内,左端点前缀和的最小值的点
把所有的这些放到一个大根堆里
取出一个元素后
若原区间[a,b] 左端点选的位置是p
那么原区间分裂为两个区间[a,p-1] 和 [p+1,b]
即 若原来区间的右端点是End,左端点可选区间为[a,b]
那么当[a,b]内选位置p当左端点,且作为前k大用过后,
右端点为End的区间可选左端点变成了 [a,p-1],[p+1,b],放入堆中即可

#include<bits/stdc++.h>
using namespace std;
const int maxx = 5e5+10;
typedef long long LL;
struct node
{
    int l,r;
    int ed,pos,val;
    bool operator < (const node &t)const
    {
        return val<t.val;
    }
};
int sum[maxx];
int mi[maxx][20];
priority_queue<node>q;
int getmin(int l,int r)
{
    int k=log2(r-l+1);
    return sum[mi[l][k]]<sum[mi[r-(1<<k)+1][k]]?mi[l][k]:mi[r-(1<<k)+1][k];
}
int main()
{
    int n,k,L,R;
    scanf("%d%d%d%d",&n,&k,&L,&R);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&sum[i]);
        sum[i]+=sum[i-1];
        mi[i][0]=i;
    }
    //rmq维护区间最小值
    for(int j=1;1<<j<=n;j++)
        for(int i=0;i+(1<<j)-1<=n;i++) //注意这道题要把0也算进去
            if(sum[mi[i][j-1]]<sum[mi[i+(1<<(j-1))][j-1]])mi[i][j]=mi[i][j-1];
            else mi[i][j]=mi[i+(1<<(j-1))][j-1];
    node tmp,res;
    for(int i=1;i<=n;i++) //固定右端点,寻找最小的左端点的前缀和
    {
        tmp.ed=i;
        tmp.l=max(i-R,0);
        tmp.r=i-L;
        if(tmp.l>tmp.r)continue;
        tmp.pos=getmin(tmp.l,tmp.r);
        tmp.val=sum[i]-sum[tmp.pos];
        q.push(tmp);
    }
    LL ans=0;
    while(k--)
    {
        tmp=q.top();
        q.pop();
        res.ed=tmp.ed;
        ans+=tmp.val;
        if(tmp.l!=tmp.pos)
        {
            res.pos=getmin(tmp.l,tmp.pos-1);
            res.l=tmp.l;
            res.r=tmp.pos-1;
            res.val=sum[res.ed]-sum[res.pos];
            q.push(res);
        }
        if(tmp.r!=tmp.pos)
        {
            res.pos=getmin(tmp.pos+1,tmp.r);
            res.l=tmp.pos+1;
            res.r=tmp.r;
            res.val=sum[res.ed]-sum[res.pos];
            q.push(res);
        }
    }
    printf("%lld\n",ans);
    return 0;
}
posted @ 2019-10-12 23:35  灰灰烟影  阅读(150)  评论(0编辑  收藏  举报