Description
兔子们在玩k个串的游戏。首先,它们拿出了一个长度为n的数字序列,选出其中的一
个连续子串,然后统计其子串中所有数字之和(注意这里重复出现的数字只被统计一次)。
兔子们想知道,在这个数字序列所有连续的子串中,按照以上方式统计其所有数字之和,第
k大的和是多少。
Input
第一行,两个整数n和k,分别表示长度为n的数字序列和想要统计的第k大的和
接下里一行n个数a_i,表示这个数字序列
Output
一行一个整数,表示第k大的和
Sample Input
7 5
3 -2 1 2 2 1 3 -2
Sample Output
4
HINT
1 <= n <= 100000, 1 <= k <= 200000, 0 <= |a_i| <= 10^9数据保证存在第 k 大的和.
解题思路:
考虑主席树,第i棵树节点(l,r)维护右端点为i,左端点在(l,r)之间的区间的最大值。
由于不计重复的数,所以每新加一个数ai,相当于在第i-1棵树的(pre[i],i)区间加了ai,所以要区间打标记,主要是注意主席树打标记时要新建节点,否则前面的树的节点也会指向这里。
然后如何维护总体第k大呢?
claris的做法:用一个大根堆维护五元组(v,x,l,r,p),表示区间和为v,右端点i所在线段树根节点为x,所选左端点范围为[l,r],左端点选了p。
开始时只把每个根节点的值求进堆,然后重复k次,每次取出堆顶,扩展出[l,m−1]以及[m+1,r]两个新状态。
#include<bits/stdc++.h>
#define ll long long
#define P pair<ll,int>
using namespace std;
int getint()
{
int i=0,f=1;char c;
for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
if(c=='-')f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
const int N=100005,M=7000005;
int n,m;
int tot,rt[N],lc[M],rc[M];P v[M];ll tag[M];
map<int,int>pre;
struct node
{
ll v;int x,l,r,p;
node(){}
node(ll _v,int _x,int _l,int _r,int _p):v(_v),x(_x),l(_l),r(_r),p(_p){}
inline friend bool operator < (const node &a,const node &b){return a.v<b.v;}
}tmp;
priority_queue<node>q;
int build(int l,int r)
{
int x=++tot;v[x]=P(0,l);
if(l==r)return x;
int mid=l+r>>1;
lc[x]=build(l,mid),rc[x]=build(mid+1,r);
return x;
}
int add(int y,ll p)
{
int x=++tot;
lc[x]=lc[y],rc[x]=rc[y],v[x]=v[y],v[x].first+=p,tag[x]=tag[y]+p;
return x;
}
void pushdown(int x)
{
lc[x]=add(lc[x],tag[x]),rc[x]=add(rc[x],tag[x]);
tag[x]=0;
}
int modify(int y,int l,int r,int ql,int qr,int p)
{
if(ql<=l&&r<=qr)return add(y,p);
if(tag[y])pushdown(y);
int x=++tot;lc[x]=lc[y],rc[x]=rc[y],v[x]=v[y];
int mid=l+r>>1;
if(ql<=mid)lc[x]=modify(lc[y],l,mid,ql,qr,p);
if(qr>mid)rc[x]=modify(rc[y],mid+1,r,ql,qr,p);
v[x]=max(v[lc[x]],v[rc[x]]);
return x;
}
P query(int x,int l,int r,int ql,int qr)
{
if(ql==l&&qr==r)return v[x];
if(tag[x])pushdown(x);
int mid=l+r>>1;
if(qr<=mid)return query(lc[x],l,mid,ql,qr);
else if(ql>mid)return query(rc[x],mid+1,r,ql,qr);
else return max(query(lc[x],l,mid,ql,mid),query(rc[x],mid+1,r,mid+1,qr));
}
void extend(int x,int l,int r)
{
if(l>r)return;
P t=query(x,1,n,l,r);
q.push(node(t.first,x,l,r,t.second));
}
int main()
{
//freopen("lx.in","r",stdin);
n=getint(),m=getint();
rt[0]=build(1,n);
for(int i=1;i<=n;i++)
{
int x=getint();
rt[i]=modify(rt[i-1],1,n,pre[x]+1,i,x);
pre[x]=i;extend(rt[i],1,i);
}
while(m--)
{
tmp=q.top(),q.pop();
extend(tmp.x,tmp.l,tmp.p-1);
extend(tmp.x,tmp.p+1,tmp.r);
}
cout<<tmp.v<<'\n';
return 0;
}