题意
给出一个长度为
n
的数列
求第k大的和
题解
首先怎么求一个区间的和.对于 ai 维护它之前最近的与它相同的数的位置 pre[ai]
对于固定的右端点维护每一个左端点到它的和,可以用线段树
则从左往右扫描对于右端点
ar
只要把
pre[ar]+1
到
r
都加上
把线段树可持久化
现在我们可以询问给出点
r
并且左端点在
现在要求第
k
大,好了,只要每次找出最大的区间再让它不能被选,选个
因为要求区间不能选重,可以套用NOI2010 超级钢琴的思想:
我们维护一种五元组
(v,rt,x,l,r)
,表示右端点所在线段树根为
rt
,左端点在
[l,r]
的最大值为
v
,取到
放入大根堆(以
v
为关键字)中,每次取出堆顶,此时这个区间就被选走了,不能再被选了,我们把它的左端点范围分成两段:
#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;
}