• 【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;
    }
  • 相关阅读:
    oracle 聚合函数 LISTAGG ,将多行结果合并成一行
    oracle 数据库对于多列求最大值
    Java 简单的rpc 一
    centos7 安装php7
    win10下VM 中centos 安装共享文件
    CentOS7 cannot find a valid baseurl for repo base
    分布式事务
    利用虚拟映射文件加密大文件
    动态代理
    c++ 11 lambda表达式
  • 原文地址:https://www.cnblogs.com/Invictus-Ocean/p/12828721.html
Copyright © 2020-2023  润新知