BZOJ4504. K个串(主席树+优先队列)

4504. K个串

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 大的和

solution

一个 [ l , r ] [l,r] [l,r]的子串的和定义为不同数的和,一个数多次出现只算一次

可以联想到Rmq Problem / mex的处理

以每个 i i i为子串右端点 r r r建立一个新版本的主席树

每个版本的叶子结点的值就是该点做左端点时,这个子串的和

先继承上一个 i − 1 i-1 i1版本

再记录上一次该值的出现位置 l s t i lst_i lsti,那么 a i a_i ai只会对 ( l s t i , i ] (lst_i,i] (lsti,i]内的数产生 a i a_i ai贡献

唯一不同的是,这里的修改是对一段区间的修改,为了线段树的复杂度正确必须使用懒标记

而主席树的懒标记,因为某些节点的共用,后面版本的懒标记下传可能会影响前面版本的答案

所以,每个新版本的懒标记下放的时候都需要复制原点,在新点上操作

问题不是求最大,而是第 K K K

这又联想到[NOI]超级钢琴从最佳点进行左右区间切割的处理

先往有先队列里面丢每个版本最大值ans,以及最大值选取的左端点位置pos,还要记录是哪个版本线段树i,以及可选取为左端点的区间l,r

当取出当前的最大值(ans,i,l,r,pos)

就会分裂成两个区间(*,i,l,pos-1,*) (*,i,pos+1,r,*)

*需要对该版本的线段树进行查询,查询区间内的最大值及最大值位置

code

调懒标记的问题,调麻了

#include <map>
#include <queue>
#include <cstdio>
#include <iostream>
using namespace std;
#define Pair pair < int, int >
#define int long long
#define maxn 100005
map < int, int > mp;
struct Node { 
	int ans, i, l, r, pos;
	bool operator < ( const Node &t ) const {
		return ans < t.ans;
	}
};
priority_queue < Node > q;
struct node { int lson, rson, Max, tag, pos; }t[maxn * 160];
int n, k, cnt;
int lst[maxn], a[maxn], root[maxn];

void pushup( int now ) {
	if( t[t[now].lson].Max > t[t[now].rson].Max ) 
		t[now].Max = t[t[now].lson].Max, t[now].pos = t[t[now].lson].pos;
	else
		t[now].Max = t[t[now].rson].Max, t[now].pos = t[t[now].rson].pos;
}

void pushdown( int now ) {
	if( ! t[now].tag ) return;
	node lson = t[t[now].lson];
	node rson = t[t[now].rson];
	t[t[now].lson = ++ cnt] = lson;
	t[t[now].rson = ++ cnt] = rson; 
	t[t[now].lson].Max += t[now].tag;
	t[t[now].lson].tag += t[now].tag;
	t[t[now].rson].Max += t[now].tag;
	t[t[now].rson].tag += t[now].tag;
	t[now].tag = 0;
}

void build( int &now, int l, int r ) {
	now = ++ cnt;
	if( l == r ) { t[now].Max = 0, t[now].pos = l; return; }
	int mid = ( l + r ) >> 1;
	build( t[now].lson, l, mid );
	build( t[now].rson, mid + 1, r );
	pushup( now );
}

void modify( int &now, int lst, int l, int r, int L, int R, int val ) {
	if( r < L or R < l ) return;
	if( L <= l and r <= R ) {
		t[now = ++ cnt] = t[lst];
		t[now].Max += val;
		t[now].tag += val;
		return;
	}
	pushdown( lst );
	t[now = ++ cnt] = t[lst];
	int mid = ( l + r ) >> 1;
	modify( t[now].lson, t[lst].lson, l, mid, L, R, val );
	modify( t[now].rson, t[lst].rson, mid + 1, r, L, R, val );
	pushup( now );
}

Pair query( int now, int l, int r, int L, int R ) {
	if( r < L or R < l ) return make_pair( -1e18, 0 );
	if( L <= l and r <= R ) return make_pair( t[now].Max, t[now].pos );
	pushdown( now );
	int mid = ( l + r ) >> 1;
	Pair lson = query( t[now].lson, l, mid, L, R );
	Pair rson = query( t[now].rson, mid + 1, r, L, R );
	return max( lson, rson );
}

signed main() {
	scanf( "%lld %lld", &n, &k );
	for( int i = 1;i <= n;i ++ ) {
		scanf( "%lld", &a[i] );
		if( mp[a[i]] ) lst[i] = mp[a[i]];
		mp[a[i]] = i;
	}
	build( root[0], 1, n );
	for( int i = 1;i <= n;i ++ ) {
		modify( root[i], root[i - 1], 1, n, lst[i] + 1, i, a[i] );
		Pair it = query( root[i], 1, n, 1, i );
		q.push( { it.first, i, 1, i, it.second } );
	}
	int ans, pos, l, r, i; Pair it;
	while( k -- ) {
		Node now = q.top(); q.pop();
		ans = now.ans, pos = now.pos, i = now.i, l = now.l, r = now.r;
		if( pos ^ l ) {
			it = query( root[i], 1, n, l, pos - 1 );
			q.push( { it.first, i, l, pos - 1, it.second } );
		}
		if( pos ^ r ) {
			it = query( root[i], 1, n, pos + 1, r );
			q.push( { it.first, i, pos + 1, r, it.second } );
		}
	}
	printf( "%lld\n", ans );
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值