[BZOJ 2006] 超级钢琴

Link:

https://www.lydsy.com/JudgeOnline/problem.php?id=2006 

Algorithm:

对于此类区间最值类问题,我们可以通过控制一端不变来寻找当前点的最值,再综合比较

此题中,在求完前缀和后,在左端点确定的情况下,只要寻找前缀和最大的右端点

为了快速寻找最优的右端点位置,我们需要RMQ来进行维护

 

由于不存在修改操作而只有查询,那么ST List  O(1)查询 O(n)修改  的特性就能充分利用

在求出前缀和后用ST list维护区间MAX即可

 

定义一个四元组(i,L,R,pos),其中,i表示固定下的左端点,L,R表示右端点存在的区间,pos表示右端点此时最优位置

为了不涉及到ST list不支持的删除操作,在选定pos后四元组分为两段:

$(i,L,R,pos)−>(i,L,pos−1,pos′)+(i,pos+1,R,pos′′)$

 

这样用优先队列每次取出最优解即可

Code:

//by NewErA
#include<bits/stdc++.h>

using namespace std;
typedef long long ll;

inline int read()
{
    char ch;int num,f=0;
    while(!isdigit(ch=getchar())) f|=(ch=='-');
    num=ch-'0';
    while(isdigit(ch=getchar())) num=num*10+ch-'0';
    return f?-num:num;
}

struct SP
{
    int i,l,r,pos;
};

const int MAXN=500005;
int n,k,L,R,a[MAXN],log_2[MAXN],st[MAXN][100];
ll res=0;

void init()
{
    log_2[1]=0;
    for(int i=2;i<=n;i++)
    {
        log_2[i]=log_2[i-1];
        if(1<<(log_2[i]+1)==i) log_2[i]++;
    }
    
    for(int i=n;i>=1;i--)
    {
        st[i][0]=i;
        for(int j=1;(i+(1<<j)-1)<=n;j++)
            if(a[st[i][j-1]]>a[st[i+(1<<(j-1))][j-1]]) st[i][j]=st[i][j-1];
            else st[i][j]=st[i+(1<<(j-1))][j-1];
    }
}

int Query(int l,int r)
{
    int x=log_2[r-l+1];
    if(a[st[l][x]]>a[st[r-(1<<x)+1][x]]) return st[l][x];
    else return st[r-(1<<x)+1][x];
}

inline bool operator < (SP x,SP y)  //运算符重载
{
    return a[x.pos]-a[x.i-1]<a[y.pos]-a[y.i-1];
} 

int main()
{
    n=read();k=read();L=read();R=read();
    for(int i=1;i<=n;i++) a[i]=read();
    for(int i=2;i<=n;i++) a[i]+=a[i-1];
    
    init();
    
    priority_queue<SP,vector<SP> > Q;
    for(int i=1;i<=n;i++)
        if(i+L-1<=n)
        {
            int t=min(i+R-1,n);
            Q.push(SP{i,i+L-1,t,Query(i+L-1,t)});
        }
    while(k--)
    {
        SP cur=Q.top();Q.pop();
        res+=a[cur.pos]-a[cur.i-1];
        if(cur.pos-1>=cur.l) Q.push(SP{cur.i,cur.l,cur.pos-1,Query(cur.l,cur.pos-1)});
        if(cur.pos+1<=cur.r) Q.push(SP{cur.i,cur.pos+1,cur.r,Query(cur.pos+1,cur.r)});
    }
    cout << res;
    return 0;
}

 

1、priority_queue的运算符重载问题(Updating)

 

2、只需要RMQ的查询操作时尽量用ST List

 

3、如果删除1个或少量数据的操作难以实现时,考虑将原数据分段,递归式地考虑每一段的情况

 

posted @ 2018-05-18 12:04  NewErA  阅读(177)  评论(0编辑  收藏  举报