超级钢琴(主席树+贪心+优先队列)

超级钢琴

我能说这是主席树板子题嘛?

题意:给定一个序列,求长度在 L L L R R R内的前 K K K大子串和。

思路

  1. 既然有子串,肯定要先搞个前缀和,离散化之类的
  2. 然后我们建好主席树
  3. 而主要的思路在于贪心,我们可以枚举子串的左端点,然后在其合法的右端点中通过主席树找到最大的右端点(子串满足前缀和关系,每个右端点都会减去这个相同的左端点的前一个值)
  4. 把枚举得到的所有最大子串都放进优先队列中(维护左端点+最大子串),每次取出队头,就在答案中加上这个值,然后再以当前左端点利用主席树求区间第 j + 1 j+1 j+1大(假设刚刚取出的是第 j j j大),又塞进优先队列即可
  5. 这样时间复杂度应该是 O ( k ∗ l o g n ) O(k*logn) O(klogn),优先队列的空间复杂度为 O ( n + k ) O(n+k) O(n+k)

题面描述

#include "bits/stdc++.h"
#define hhh printf("hhh\n")
#define see(x) (cerr<<(#x)<<'='<<(x)<<endl)
using namespace std;
typedef long long ll;
typedef pair<int,int> pr;
inline int read() {int x=0,f=1;char c=getchar();while(c!='-'&&(c<'0'||c>'9'))c=getchar();if(c=='-')f=-1,c=getchar();while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();return f*x;}

const int maxn = 5e5+10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
const double eps = 1e-7;

int n, nn, K, L, R;
int a[maxn], b[maxn];
int root[maxn], ls[maxn<<5], rs[maxn<<5];
int sz[maxn<<5], sum[maxn<<5], tot;

void update(int x, int l, int r, int pre, int &now) {
    now=++tot;
    ls[now]=ls[pre], rs[now]=rs[pre], sz[now]=sz[pre]+1;
    if(l==r) return;
    int m=(l+r)/2;
    if(x<=m) update(x,l,m,ls[pre],ls[now]);
    else update(x,m+1,r,rs[pre],rs[now]);
}

int qk(int k, int x, int y, int l, int r) {
    if(l==r) return b[l];
    int s0=sz[y]-sz[x];
    if(k>s0) return -inf;
    int s=sz[rs[y]]-sz[rs[x]], m=(l+r)/2;
    if(s>=k) return qk(k,rs[x],rs[y],m+1,r);
    else return qk(k-s,ls[x],ls[y],l,m);
}

struct P{
    int st, v;
    bool operator < (const P &rhs) const {
        return v<rhs.v;
    }
};

int mxk[maxn];
priority_queue<P> q;

int main() {
    //ios::sync_with_stdio(false); cin.tie(0);
    n=read(), K=read(), L=read(), R=read();
    for(int i=1; i<=n; ++i) b[i]=a[i]=a[i-1]+read();
    sort(b+1,b+1+n); nn=unique(b+1,b+1+n)-b-1;
    for(int i=1; i<=n; ++i) a[i]=lower_bound(b+1,b+1+nn,a[i])-b;
    for(int i=1; i<=n; ++i) update(a[i],1,nn,root[i-1],root[i]);
    for(int i=1; i<=n-L+1; ++i) q.push((P){i,qk(++mxk[i],root[i+L-2],root[min(n,i+R-1)],1,nn)-b[a[i-1]]});
    int cnt=0; ll ans=0;
    while(1) {
        P now=q.top(); q.pop();
        ans+=now.v; if(++cnt==K) break;
        q.push((P){now.st,qk(++mxk[now.st],root[now.st+L-2],root[min(n,now.st+R-1)],1,nn)-b[a[now.st-1]]});
    }
    printf("%lld\n", ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值