【bzoj4504】k个串
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\n",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\n",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();
}