【bzoj4504】k个串

Portal -->bzoj4504

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();
}
posted @ 2018-07-16 16:26  yoyoball  阅读(310)  评论(0编辑  收藏  举报