Description
给你一个数列,定义一个连续子串的和为这个子串中所有的数字之和(相同的数字只算一次),求这个数列的所有连续子串的和中第(k)大的和是多少
范围:(1<=n<=10^5,1<=k<=2*10^5,0<=|a_i|<=10^9),保证有解
Solution
额因为是权限题所以简单写一下题面。。
感觉自己好像还是不太。。会用这种统计的思路呀qwq就是固定一端看另一端的qwq所以还是没有在场上想出来qwq以及求第(k)大不是只有二分之类的方法的好吧Q^Q怎么把最简单粗暴的那种给忘了qwq
(注意:以下涉及到的所有的子串和指的都是相同数字只算一次的)
首先看看如果说不是第(k)大而是最大要怎么做
可以有一个比较简单粗暴的想法,我们记(f[i])为以位置(i)为连续子串左端点的子串和的大小,那么答案显然就是(max(f[i]))
那如果是要求第(k)大怎么办呢?
那也有一个很简单粗暴的想法,就是不停删掉当前最大的那个(f[i]),然后用其次小值来替代,这样操作(k-1)次之后,(max(f[i]))就是我们要的答案了
这部分的实现可以用一个堆来维护(或者直接调用优先队列)
现在的问题是怎么维护(f[i])
显然离散化之后(f[1])是可以直接计算的,因为我们可以很轻易地算出以(1)为左端点的每个连续子串的和,为了方便接下来的表述,记为(b_1[i])为连续子串([1,i])的子串和
然后接着看看如何得到(f[2])
我们记(nxt[i])表示(a[i])这个数在(i)这个位置的下一个出现位置的下标,那么我们会发现当子串从(2)开始的时候,我们的(b_2[i])中只有([2,nxt[a[1]]))这个区间中的值会受到影响,这个范围内的(b_2[i])都要(-a[1]),并且(b_2[1])会变得不合法,而不在这个范围以内的(b_2[i])的值则与(b_1[i])相同
同理我们可以由(b_i)推得(b_{i+1})
那所以我们可以考虑用主席树来维护(f[i])了,只要把(b[i])的值丢进去然后求个最大值就好了,具体一点就是:从以(i)为左端点到以(i+1)为左端点,我们只需要对区间([i,nxt[a[i-1]))减去(a[i-1]),将(i-1)这个位置减去(inf)(也就是保证这个位置不会成为最大值)就好了,同理如果说是删掉最大值的话,我们对最大值那个位置减去(inf)即可
这样一来我们就可以快速查得(f[i])以及删掉最大值之后的答案了,加个优先队列或者堆这题就很愉快滴做完啦ovo
因为是可持久化的主席树,区间修改我们标记永久化一下(然而貌似下传也可以就是空间会开大许多),注意递归传参的时候有一点点小不同
代码大概长这个样子
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define ll long long
using namespace std;
const int N=100010,SEG=N*100;
const int ll inf=1e15;
struct Data{
int rt,loc;
ll val;
Data(){}
Data(int _rt,int _loc,ll _val){rt=_rt; loc=_loc; val=_val;}
friend bool operator < (Data x,Data y)
{return x.val<y.val;}
};
int a[N],V[N],Lis[N],loc[N],nxt[N];
ll b[N];
int vis[N];
int n,m;
priority_queue<Data> q;
namespace Seg{/*{{{*/
int ch[SEG][2],rt[N*3],loc[SEG];
ll mx[SEG],tag[SEG];
int tot,n;
int newnode(int pre){
ch[++tot][0]=ch[pre][0]; ch[tot][1]=ch[pre][1]; tag[tot]=tag[pre]; mx[tot]=mx[pre];
loc[tot]=loc[pre];
return tot;
}
void pushup(int x){
if (mx[ch[x][0]]+tag[ch[x][0]]>mx[ch[x][1]]+tag[ch[x][1]])
mx[x]=mx[ch[x][0]]+tag[ch[x][0]],loc[x]=loc[ch[x][0]];
else
mx[x]=mx[ch[x][1]]+tag[ch[x][1]],loc[x]=loc[ch[x][1]];
}
void _build(int x,int l,int r){
tag[x]=0; mx[x]=0;
if (l==r){mx[x]=b[l];loc[x]=l; return;}
int mid=l+r>>1;
ch[x][0]=++tot; _build(ch[x][0],l,mid);
ch[x][1]=++tot; _build(ch[x][1],mid+1,r);
pushup(x);
}
void build(int _n){n=_n; tot=rt[1]=1; _build(1,1,n);}
void _update(int pre,int &x,int l,int r,int lx,int rx,ll delta){
x=newnode(pre);
if (l<=lx&&rx<=r){tag[x]+=delta;return;}
int mid=lx+rx>>1;
if (r<=mid) _update(ch[pre][0],ch[x][0],l,r,lx,mid,delta);
else if (l>mid) _update(ch[pre][1],ch[x][1],l,r,mid+1,rx,delta);
else {
_update(ch[pre][0],ch[x][0],l,mid,lx,mid,delta);
_update(ch[pre][1],ch[x][1],mid+1,r,mid+1,rx,delta);
}
pushup(x);
}
void update(int pre,int x,int l,int r,ll delta){_update(rt[pre],rt[x],l,r,1,n,delta);}
Data query(int x){return Data(x,loc[rt[x]],mx[rt[x]]+tag[rt[x]]);}
}/*}}}*/
void prework(int n){
sort(Lis+1,Lis+1+Lis[0]);
Lis[0]=unique(Lis+1,Lis+1+Lis[0])-Lis-1;
for (int i=1;i<=n;++i){
int x=lower_bound(Lis+1,Lis+1+Lis[0],a[i])-Lis;
V[x]=a[i]; a[i]=x;
}
for (int i=1;i<=n;++i) nxt[i]=n+1;
for (int i=1;i<=n;++i){
if (loc[a[i]])
nxt[loc[a[i]]]=i;
loc[a[i]]=i;
}
memset(vis,0,sizeof(vis));
for (int i=1;i<=n;++i){
if (!vis[a[i]]) b[i]=b[i-1]+V[a[i]];
else b[i]=b[i-1];
vis[a[i]]=1;
}
}
void solve(){
Seg::build(n);
for (int i=2;i<=n;++i){
if (i<=nxt[i-1]-1)
Seg::update(i-1,i,i,nxt[i-1]-1,-V[a[i-1]]);
else
Seg::update(i-1,i,1,n,0);
Seg::update(i,i,i-1,i-1,-inf);
}
for (int i=1;i<=n;++i){
q.push(Seg::query(i));
//printf("%d %d
",Seg::loc[Seg::rt[i]],Seg::mx[Seg::rt[i]]);
}
Data now;
for (int i=1;i<m;++i){
now=q.top(); q.pop();
Seg::update(now.rt,n+i,now.loc,now.loc,-inf);
q.push(Seg::query(n+i));
}
now=q.top();
printf("%lld
",now.val);
}
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i)
scanf("%d",a+i),Lis[++Lis[0]]=a[i];
prework(n);
solve();
}