【NOI2010】超级钢琴 题解(贪心+堆+ST表)

题目链接

 

题目大意:求序列内长度在$[L,R]$范围内前$k$大子序列之和。

----------------------

 

考略每个左端点$i$,合法的区间右端点在$[i+L,i+R]$内。

不妨暴力枚举所有左端点,找到以其为左端点满足条件的最大子序列。

用贪心的思想,这些序列一定是满足题意的。统计后将该序列删除。

但这样直接删除肯定会丢失一部分有用的序列。一些也在前$k$大范围内的序列可能跟删除部分有交集。

所以我们不妨设$maxi$指以$i$为左端点的子序列,以$maxi$为右端点时能取得最大值。从此处将原先的大区间$[i+L,i+R]$裂解成两个区间$[i+L,maxi-1]$和$[maxi+1,i+R]$。从这两个区间中再找较大的满足条件的序列。

维护显然用堆。设一个五元组$(v,i,l,r,w)$表示以$i$为左端点,右端点在$[i+L,i+R]$内时,能找出以$w$为右端点的最大值为$v$的子序列。以$v$作为关键字,建立大根堆维护即可。

最后一个问题就是查找。我们不妨预处理出前缀和,用前缀和查找子序列的和。最大值用$ST$表维护。

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=500005;
int f[N][21],pos[N][21];
int n,k,l,r,op;
long long ans;
int maxn,maxi;
struct node
{
    int v,i,l,r,w;
}t,tmp;
bool operator <(const node &a,const node &b)
{
    return a.v<b.v;
}
priority_queue<node> q;
inline void RMQ(int l,int r)
{
    int opt=log2(r-l+1);
    if (f[l][opt]>=f[r-(1<<opt)+1][opt]) maxn=f[l][opt],maxi=pos[l][opt];
    else maxn=f[r-(1<<opt)+1][opt],maxi=pos[r-(1<<opt)+1][opt];
}
signed main()
{
    memset(f,128,sizeof(f));
    f[0][0]=0;
    scanf("%lld%lld%lld%lld",&n,&k,&l,&r);
    op=log2(n);
    for (int i=1;i<=n;i++)
    {
        scanf("%lld",&f[i][0]);
        pos[i][0]=i;
        f[i][0]+=f[i-1][0];
    }
    for (int t=1;t<=op;t++)
        for (int i=1;i<=n;i++) if (i+(1<<(t-1))-1>n) break;
        else
        {
            if (f[i][t-1]>=f[i+(1<<(t-1))][t-1]) f[i][t]=f[i][t-1],pos[i][t]=pos[i][t-1];
            else f[i][t]=f[i+(1<<(t-1))][t-1],pos[i][t]=pos[i+(1<<(t-1))][t-1];
        }
    for (int i=1;i<=n-l+1;i++)
    {
        RMQ(i+l-1,i+min(n-i,r-1));
        t.v=maxn-f[i-1][0],t.i=i,t.l=i+l-1,t.r=i+min(n-i,r-1),t.w=maxi;
        q.push(t);
    }
    while(k--)
    {
        t=q.top();
        q.pop();
        ans+=t.v;
        if (t.w>t.l)
        {
            RMQ(t.l,t.w-1);
            tmp.v=maxn-f[t.i-1][0],tmp.i=t.i,tmp.l=t.l,tmp.r=t.w-1,tmp.w=maxi;
            q.push(tmp);
        }
        if (t.w<t.r)
        {
            RMQ(t.w+1,t.r);
            tmp.v=maxn-f[t.i-1][0],tmp.i=t.i,tmp.l=t.w+1,tmp.r=t.r,tmp.w=maxi;
            q.push(tmp);
        }
    }
    printf("%lld",ans);
    return 0;
}

 

上一篇:C++如何使用自定义函数改变数组元素顺序


下一篇:HDU 2019 Multi-University Training Contest 10 简要题解?