【题解】NOI-2010 超级钢琴

Problem

bzoj
洛谷
vjudge

Solution

题意:给定数列{ ai a i },求 k k 个/子串/的/子串和/的/和,其中k个子串满足:每个子串长度[l,r] k k 个子串互不相同

这题求解的是k个不同的值相加,总值最大,看上去有点像SDOI-2010-魔法猪学院- k k 短路(这两题好像是同一年的),所以根据后者的解法,能比较自然地想到使用堆求解(对情况建立大根堆,弹出前k个)

然后还有堆内元素的转换( k k 短路中是删除一个点的状态,增加从该点扩散的点的状态),可以用(i,l,r,pos)表示左端点在 i i ,右端点在[l,r],在当前左右端点情况下最大前缀和(从 ai a i 算起),每次取出这种最优情况,然后将这种情况劈开

pos p o s 劈开,
(i,l,r,pos) ( i , l , r , p o s )
劈成
(i,l,pos1,query(l,pos1)) ( i , l , p o s − 1 , q u e r y ( l , p o s − 1 ) )

(i,pos+1,r,query(pos+1,r)) ( i , p o s + 1 , r , q u e r y ( p o s + 1 , r ) )

其中 query(x,y) q u e r y ( x , y ) [x,y] [ x , y ] 前缀和极大值的位置

可以使用线段树或 st s t 表,推荐 st s t 表,没卡线段树,也行

详见代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rg register
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
#define get_(x,y) (s[(x)]>s[(y)]?(x):(y))
#define cl(x) memset(x,0,sizeof(x))

template <typename _Tp> inline void read(_Tp&x){
    rg char c11=getchar(),ob=0;x=0;
    while(c11!='-'&&!isdigit(c11))c11=getchar();if(c11=='-')c11=getchar(),ob=1;
    while(isdigit(c11))x=x*10+c11-'0',c11=getchar();if(ob)x=-x;return ;
}

const int N=505000,LOG=20;
int st[N][LOG],s[N],Log[N];
int n,L,R,K;
struct data{int i,l,r,pos;};

void init();

inline int query(int l,int r){
    if(l==r)return l;
    int pos=Log[r-l+1];
    return get_(st[l][pos],st[r-(1<<pos)+1][pos]);
}

priority_queue <data,vector<data> > q;

inline char operator < (data x,data y){return s[x.pos]-s[x.i-1]<s[y.pos]-s[y.i-1];}

void work(){
    data now,r;
    rg ll ans=0;
    for(rg int i=1;i+L-1<=n;++i)
        now.i=i,
        now.l=i+L-1,
        now.r=min(n,i+R-1),
        now.pos=query(i+L-1,min(n,i+R-1)),
        q.push(now);
    while(K--){
        data now=q.top();q.pop();
        ans+=s[now.pos]-s[now.i-1];
        if(now.l<now.pos)
            r.i=now.i,
            r.l=now.l,
            r.r=now.pos-1,
            r.pos=query(now.l,now.pos-1),
            q.push(r);
        if(now.pos<now.r)
            r.i=now.i,
            r.l=now.pos+1,
            r.r=now.r,
            r.pos=query(now.pos+1,now.r),
            q.push(r);
    }
    printf("%lld\n",ans);
    return ;
}

int main(){
    init();
    work();
    return 0;
}

void init(){
    read(n),read(K),read(L),read(R);
    for(rg int i=1;i<=n;++i) read(s[i]),s[i]+=s[i-1];

    Log[0]=-1;
    for(rg int i=1;i<=n;++i) Log[i]=Log[i>>1]+1,st[i][0]=i;

    for(rg int i=n;i;--i)
    for(rg int j=1;i+(1<<j)-1<=n;++j)
        st[i][j]=get_(st[i][j-1],st[i+(1<<(j-1))][j-1]);
    return ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值