BZOJ2006: 超级钢琴 题解

非常好的锻炼思维的题目
这样的题目的一般套路肯定是将这些超级旋律分成若干类,刚开始在每一类里面选一个最大的放进优先队列,每次取出最大的一个以后在它所在的类中再挑几个candidate扔进优先队列
我们可以考虑把起点相同的超级旋律放在一起考虑
我们定义操作(i,l,r)返回起点在i,终点在l~r之间的所有超级旋律中值最大的超级旋律的值和位置
刚开始我们对每个起点i,把(i,i+L-1,i+R-1)返回的位置和值扔进优先队列(注意边界),然后连续进行k次取最大
假设当前我们取出了一个最大值,对应的起点是starter,是从l~r中选的,最大值对应的位置是pos
那我们就调用(starter,l,pos-1)和(starter,pos+1,r),把这两个扔进优先队列,这样这个题目就解决了
最后我们来看这个操作怎么实现,我们会发现在l~r中取最大值和starter的位置是无关的,因为大家要多一起多,要少一起少,所以我们只要在原数列的前缀和数列上取l~r位置的max就好
为了保证复杂度,用ST表实现,这样总复杂度 O(nlogn) O ( n l o g n )

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include <utility>
#include <cctype>
#include <algorithm>
#include <bitset>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <deque>
#include <stack>
#include <cmath>
#define LL long long
#define LB long double
#define x first
#define y second
#define Pair pair<int,int>
#define pb push_back
#define pf push_front
#define mp make_pair
#define LOWBIT(x) x & (-x)
using namespace std;

const int MOD=100003;
const LL LINF=2e16;
const int INF=1e9;
const int magic=348;
const double eps=1e-10;

inline int getint()
{
    char ch;int res;bool f;
    while (!isdigit(ch=getchar()) && ch!='-') {}
    if (ch=='-') f=false,res=0; else f=true,res=ch-'0';
    while (isdigit(ch=getchar())) res=res*10+ch-'0';
    return f?res:-res;
}

int n,L,R,k;
int a[500048],sum[500048];

namespace ST
{
    int ST[500048][21],pos[500048][21],tolen[500048],maxlen;
    inline void init()
    {
        int i,j;
        maxlen=0;
        for (i=1;i<=n;i++)
        {
            if ((1<<(maxlen+1))<=i) maxlen++;
            tolen[i]=maxlen;
        }
        for (i=1;i<=n;i++) ST[i][0]=sum[i],pos[i][0]=i;
        for (j=1;j<=maxlen;j++)
            for (i=1;i<=n;i++)
            {
                ST[i][j]=ST[i][j-1];pos[i][j]=pos[i][j-1];
                if (i+(1<<(j-1))<=n)
                    if (ST[i][j]<ST[i+(1<<(j-1))][j-1])
                    {
                        ST[i][j]=ST[i+(1<<(j-1))][j-1];
                        pos[i][j]=pos[i+(1<<(j-1))][j-1];
                    }
            }
    }
    inline Pair query(int starter,int left,int right)
    {
        int curlen=tolen[right-left+1];Pair res;
        if (ST[left][curlen]>ST[right-(1<<curlen)+1][curlen])
            res=mp(ST[left][curlen],pos[left][curlen]);
        else
            res=mp(ST[right-(1<<curlen)+1][curlen],pos[right-(1<<curlen)+1][curlen]);
        res.x-=sum[starter-1];return res;
    }
}

struct node
{
    int val;
    int starter,left,right,pos;
    inline bool operator < (const node &x) const {return val<x.val;}
};
priority_queue<node> q;

int main ()
{
    int i,l,r;
    n=getint();k=getint();L=getint();R=getint();
    for (i=1;i<=n;i++) a[i]=getint(),sum[i]=sum[i-1]+a[i];
    ST::init();node ins;
    for (i=1;i<=n;i++)
    {
        if (i+L-1>n) continue;
        l=i+L-1;r=min(n,i+R-1);
        Pair res=ST::query(i,l,r);
        ins=node{res.x,i,l,r,res.y};q.push(ins);
    }
    LL ans=0;
    for (register int cnt=1;cnt<=k;cnt++)
    {
        node res=q.top();q.pop();
        ans+=res.val;
        if (res.pos>res.left)
        {
            ins=res;ins.right=res.pos-1;
            Pair rr=ST::query(ins.starter,ins.left,ins.right);
            ins.val=rr.x;ins.pos=rr.y;
            q.push(ins);
        }
        if (res.pos<res.right)
        {
            ins=res;ins.left=res.pos+1;
            Pair rr=ST::query(ins.starter,ins.left,ins.right);
            ins.val=rr.x;ins.pos=rr.y;
            q.push(ins);
        }
    }
    printf("%lld\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值