BZOJ 2006: [NOI2010]超级钢琴 [ST表+堆 | 主席树]


题意:
一个序列,求k个不相同的长度属于\([L,R]\)的区间使得和最大


前缀和,对于每个r找最小的a[l]
然后我yy了一个可持久化线段树做法...也许会T
实际上主席树就可以了,区间k小值
然后看hzwer题解发现还有更有趣的做法,差一点就想到了

\((l,r,x)\)表示左端点在\([l,r]\),右端点为\(x\)的最大和
用优先队列维护
取出\((l,r,x)\)后,区间被分成两段,再加入就行了

MD我连ST表都写错

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N=5e5+5, INF=1e9;
#define pii pair<int, int>
#define MP make_pair 
#define fir first
#define sec second
typedef long long ll;
inline int read(){
    char c=getchar();int x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}

int n, k, L, R, a[N], Log[N];
struct meow{
    int x, l, r; pii v;
    bool operator <(const meow &a) const {return v < a.v;}
    meow(){}
    meow(int a, int b, int c, pii d):x(a), l(b), r(c), v(d){}
};
priority_queue<meow> q;
pii f[N][21], t;
void ini() {
    for(int i=0; i<=n; i++) f[i][0] = MP(a[i], i);
    for(int i=n; i>=0; i--)
        for(int j=1; i + (1<<j) -1 <=n; j++)
            f[i][j] = min(f[i][j-1], f[i+(1<<(j-1))][j-1]);// printf("f %d %d %d\n",i,j,f[i][j].fir);
}
inline pii Min(int x, int y) { 
    int t = Log[y-x+1]; //printf("\nMin %d %d  %d\n",x,y, t);
    return min(f[x][t], f[y - (1<<t) + 1][t]);
}
int main() {
    freopen("in","r",stdin);
    n=read(); k=read(); L=read(); R=read();
    Log[1] = 0;
    for(int i=2; i<=n; i++) Log[i] = Log[i>>1] + 1;
    for(int i=1; i<=n; i++) a[i] = read()+a[i-1];
    ini();
    for(int i=L; i<=n; i++) {
        int l = max(0, i-R), r = i-L;
        t=Min(l, r); 
        q.push(meow(i, l, r, MP(a[i] - t.fir, t.sec) ));
    }

    ll ans=0;
    while(k--) {
        meow now = q.top(); q.pop();
        ans += now.v.fir;
        int x = now.x, l = now.l, r = now.r, p = now.v.sec;
        if(l < p) t=Min(l, p-1), q.push(meow(x, l, p-1, MP(a[x] - t.fir, t.sec) ));
        if(p < r) t=Min(p+1, r), q.push(meow(x, p+1, r, MP(a[x] - t.fir, t.sec) ));
    }
    printf("%lld", ans);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值