ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2020-05-04 23:05:09  阅读:263  来源: 互联网

标签:tmp int 题解 long ST maxi NOI2010 端点 序列


题目链接

 

题目大意:求序列内长度在$[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;
}

 

标签:tmp,int,题解,long,ST,maxi,NOI2010,端点,序列
来源: https://www.cnblogs.com/Invictus-Ocean/p/12828721.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有