BZOJ 4504:K个串 [主席树][贪心]

3 篇文章 0 订阅
3 篇文章 0 订阅

题意

给出一个长度为 n 的数列an,一个区间 [l,r] 的和定义为其中的数字之和,相同的数字不重复算

求第k大的和

题解

首先怎么求一个区间的和.对于 ai 维护它之前最近的与它相同的数的位置 pre[ai]

对于固定的右端点维护每一个左端点到它的和,可以用线段树

则从左往右扫描对于右端点 ar 只要把 pre[ar]+1 r 都加上ar就好了

把线段树可持久化

现在我们可以询问给出点 r 并且左端点在[i,j]中和最大的区间

现在要求第 k 大,好了,只要每次找出最大的区间再让它不能被选,选个k

因为要求区间不能选重,可以套用NOI2010 超级钢琴的思想:

我们维护一种五元组 (v,rt,x,l,r) ,表示右端点所在线段树根为 rt ,左端点在 [l,r] 的最大值为 v ,取到v时左端点在x

放入大根堆(以 v 为关键字)中,每次取出堆顶,此时这个区间就被选走了,不能再被选了,我们把它的左端点范围分成两段:[l,x) (x,r] ,询问得出 v x再压入堆中

#include<cstdio>
#include<algorithm>
#include<queue>
#define N 100005
#define M 7000000
#define LL long long
#define INF 2e9
using namespace std;

int n,k,size,num[N],pre[N],ls[M],rs[M],rt[N];
LL add[M];
struct node{
    int x,pos;
    bool operator < (const node &b) const {
        return x<b.x||(x==b.x&&pos<b.pos);
    }
}a[N];
struct tree{
    LL sum;
    int p;
}t[M];
struct Heap{
    LL v;int rt,x,l,r;
    bool operator < (const Heap &x) const {
        return v<x.v;
    }
};
priority_queue<Heap>Q;

inline int read(){
    int a=0;char f=1,c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}
    return a*f;
}

inline tree max(tree x,tree y){return x.sum>y.sum?x:y;}

#define mid (l+r>>1)

void build(int &x,int l,int r){
    x=++size;
    if(l==r){
        t[x].p=l;
        t[x].sum=0;
        return;
    }
    build(ls[x],l,mid),build(rs[x],mid+1,r);
    t[x]=max(t[ls[x]],t[rs[x]]);
    return;
}

void insert(int &x,int lst,int l,int r,int ql,int qr,int c){
    x=++size;
    ls[x]=ls[lst],rs[x]=rs[lst];
    t[x]=t[lst],add[x]=add[lst];
    if(l==ql&&r==qr){
        t[x].sum+=c,add[x]+=c;
        return;
    }
    if(qr<=mid) insert(ls[x],ls[lst],l,mid,ql,qr,c);
    else if(ql>mid) insert(rs[x],rs[lst],mid+1,r,ql,qr,c);
    else insert(ls[x],ls[lst],l,mid,ql,mid,c),insert(rs[x],rs[lst],mid+1,r,mid+1,qr,c);
    t[x]=max(t[ls[x]],t[rs[x]]),t[x].sum+=add[x];
    return;
}

tree query(int x,int l,int r,int ql,int qr){
    if(!x) return (tree){-INF,0};
    if(l==ql&&r==qr) return t[x];
    tree res;
    if(qr<=mid) res=query(ls[x],l,mid,ql,qr);
    else if(ql>mid) res=query(rs[x],mid+1,r,ql,qr);
    else res=max(query(ls[x],l,mid,ql,mid),query(rs[x],mid+1,r,mid+1,qr));
    res.sum+=add[x];
    return res;
}

int main(){
//  freopen("testdata.in","r",stdin);
    n=read(),k=read();
    for(int i=1;i<=n;++i){
        num[i]=read();
        a[i]=(node){num[i],i};
    }
    sort(a+1,a+n+1);
    for(int i=1;i<=n;++i)
        if(a[i].x==a[i-1].x) pre[a[i].pos]=a[i-1].pos;
        else pre[a[i].pos]=0;
    build(rt[0],1,n);
    for(int i=1;i<=n;++i){
        insert(rt[i],rt[i-1],1,n,pre[i]+1,i,num[i]);
        tree tmp=query(rt[i],1,n,1,i);
        Q.push((Heap){tmp.sum,rt[i],tmp.p,1,i});
    }
    Heap tmp;tree tmp1;
    for(int i=1;i<k;++i){
        tmp=Q.top();Q.pop();
        if(tmp.l==tmp.r) continue;
        if(tmp.l<tmp.x){
            tmp1=query(tmp.rt,1,n,tmp.l,tmp.x-1);
            Q.push((Heap){tmp1.sum,tmp.rt,tmp1.p,tmp.l,tmp.x-1});
        }
        if(tmp.r>tmp.x){
            tmp1=query(tmp.rt,1,n,tmp.x+1,tmp.r);
            Q.push((Heap){tmp1.sum,tmp.rt,tmp1.p,tmp.x+1,tmp.r});
        }
    }
    printf("%lld\n",Q.top().v);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值