bzoj2006: [NOI2010]超级钢琴

也是一道好题呀。

首先先把它转换成前缀和应该都想到了。

然后用st表把s的最小值的id存下来,那么想一想,对于当前的位置rid,s[rid]-s[lid](其中rid-R+1<=lid<=rid-L+1)就是一个和弦的方案,用优先队列维护s[rid]-s[lid]最大值,跑k次。

但是当当前这个lid已用时,我们怎样找到对于rid区间次大的s呢

我们在结构体再记录一个区间fl,fr表示当前这个lid是在fl到fr这个区间里的,当我们的堆顶找到了它,在记录答案的同时,把区间分裂成fl~lid-1和lid+1~fr两部分,再次进队列就可以了,相当于筛去了lid这个位置。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
const int inf=2147483647;
typedef long long LL;

int Bin[30];
int s[510000],id[21][500100];
int getmin(int l,int r)
{
    if(l<1)l=1;
    int ret=-1;
    for(int j=20;j>=0;j--)
        if(r-l+1>=Bin[j])
        {
            if(ret==-1||s[ret]>s[id[j][l]])
                ret=id[j][l];
            l+=Bin[j];
        }
    return ret;
}

struct node
{
    int fl,fr,lid,rid;
    friend bool operator <(node n1,node n2)
    {
        return (s[n1.rid]-s[n1.lid])<(s[n2.rid]-s[n2.lid]);
    }
};
priority_queue<node>q;
int main()
{
    Bin[0]=1;for(int i=1;i<=25;i++)Bin[i]=Bin[i-1]*2;
    
    int n,K,L,R;
    scanf("%d%d%d%d",&n,&K,&L,&R);
    s[1]=0;int x;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        s[i+1]=s[i]+x;
    }
    
    for(int i=1;i<=n;i++)id[0][i]=i;
    for(int j=1;j<=20;j++)
        for(int i=1;(i+Bin[j]-1)<=n;i++)
            if(s[id[j-1][i]]>s[ id[j-1][i+Bin[j-1]] ])
                id[j][i]=id[j-1][i+Bin[j-1]];
            else
                id[j][i]=id[j-1][i];
            
    for(int i=L;i<=n;i++)
    {
        node nn;
        nn.fl=max(1,i-R+1);
        nn.fr=i-L+1;
        nn.lid=getmin(nn.fl,nn.fr);
        nn.rid=i+1;
        q.push(nn);
    }
    
    LL ans=0;
    while(K--)
    {
        node nn=q.top();q.pop();
        ans+=LL(s[nn.rid]-s[nn.lid]);
        
        node tt;
        if(nn.lid+1<=nn.fr)
        {
            tt.fl=nn.lid+1;
            tt.fr=nn.fr;
            tt.lid=getmin(tt.fl,tt.fr);
            tt.rid=nn.rid;
            q.push(tt);
        }
        if(nn.lid-1>=nn.fl)
        {
            tt.fl=nn.fl;
            tt.fr=nn.lid-1;
            tt.lid=getmin(tt.fl,tt.fr);
            tt.rid=nn.rid;
            q.push(tt);
        }
    }
    printf("%lld\n",ans);
    return 0;
}

 

posted @ 2018-03-28 13:56  AKCqhzdy  阅读(133)  评论(0编辑  收藏  举报