BZOJ2006:[NOI2010]超级钢琴

浅谈\(RMQ\)https://www.cnblogs.com/AKMer/p/10128219.html

题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=2006

首先,序列\([l,r]\)的和可以转化成前缀和相减:\(sum[r]-sum[l-1]\)

其次,对于每个右端点\(pos\),有效左端点都是一段区间\([pos-r,pos-l]\)。而这里面前缀和最小的则是最优左端点。

一开始我们将所有的四元组\((v,pos,l,r)\)丢到大根堆里。这个四元组表示以\(pos\)为右端点,有效左端点区间在\([l,r]\)内,最大值为\(v\)。假设最优左端点是\(pos'\),那么对于最优解,我们只需要执行如下步骤\(k\)次即可:

取出堆顶,\(ans+=v\)

\((sum[pos]-query(l,pos'-1),pos,l,pos'-1)\)\((sum[pos]-query(pos'+1,r),pos,pos'+1,r)\)放回堆里。这样可以保证\([pos',pos]\)不会再被取出。

\(query(l,r)\)表示求区间\([l,r]\)的前缀和最小值。这样,问题就得到了完美解决。

时间复杂度:\(O(nlogn)\)

空间复杂度:\(O(nlogn)\)

代码如下:

#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
 
const int maxn=5e5+5;
 
ll ans;
int n,limit,L,R;
int a[maxn],Log[maxn],f[20][maxn];
 
int read() {
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    return x*f;
}
 
struct node {
    int v,pos,l,r;
 
    node() {}
 
    node(int _v,int _pos,int _l,int _r) {
        v=_v,pos=_pos,l=_l,r=_r;
    }
 
    bool operator<(const node &a)const {
        return v<a.v;
    }
};
 
struct Heap {
    int tot;
    node tree[maxn<<1];
 
    void ins(node a) {
        tree[++tot]=a;
        int pos=tot;
        while(pos>1) {
            if(tree[pos>>1]<tree[pos])
                swap(tree[pos>>1],tree[pos]),pos>>=1;
            else break;
        }
    }
 
    node pop() {
        node res=tree[1];
        tree[1]=tree[tot--];
        int pos=1,son=2;
        while(son<=tot) {
            if(son<tot&&tree[son]<tree[son|1])son|=1;
            if(tree[pos]<tree[son])
                swap(tree[pos],tree[son]),pos=son,son=pos<<1;
            else break;
        }
        return res;
    }
}T;//手写堆carry你进第一版,值得拥有!
 
int fake(int num1,int num2) {
    if(a[num1]<a[num2])return num1;
    return num2;
}
 
int query(int l,int r) {
    int x=Log[r-l+1];
    return fake(f[x][l],f[x][r-(1<<x)+1]);
}
 
int main() {
    n=read(),limit=read();
    L=read(),R=read();Log[0]=-1;
    for(int i=1;i<=n;i++) {
        f[0][i]=i;
        Log[i]=Log[i>>1]+1;
        a[i]=read()+a[i-1];
    }
    for(int i=1;i<=19;i++)
        for(int j=0;j+(1<<i)-1<=n;j++)
            f[i][j]=fake(f[i-1][j],f[i-1][j+(1<<(i-1))]);
    for(int i=L;i<=n;i++) {
        int l=max(0,i-R),r=i-L;
        T.ins(node(a[i]-a[query(l,r)],i,l,r));
    }
    for(int i=1;i<=limit;i++) {
        node tmp=T.pop();
        ans+=tmp.v;
        int pos=query(tmp.l,tmp.r);
        if(pos-1>=tmp.l)
            T.ins(node(a[tmp.pos]-a[query(tmp.l,pos-1)],tmp.pos,tmp.l,pos-1));
        if(pos+1<=tmp.r)
            T.ins(node(a[tmp.pos]-a[query(pos+1,tmp.r)],tmp.pos,pos+1,tmp.r));
    }
    printf("%lld\n",ans);
    return 0;
}
posted @ 2018-12-16 21:39  AKMer  阅读(130)  评论(0编辑  收藏  举报