4504. K个串
- description
- solution
- code
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的处理
以每个iii为子串右端点rrr建立一个新版本的主席树
每个版本的叶子结点的值就是该点做左端点时,这个子串的和
先继承上一个i−1i-1i−1版本
再记录上一次该值的出现位置lstilst_ilsti,那么aia_iai只会对(lsti,i](lst_i,i](lsti,i]内的数产生aia_iai贡献
唯一不同的是,这里的修改是对一段区间的修改,为了线段树的复杂度正确必须使用懒标记
而主席树的懒标记,因为某些节点的共用,后面版本的懒标记下传可能会影响前面版本的答案
所以,每个新版本的懒标记下放的时候都需要复制原点,在新点上操作
问题不是求最大,而是第KKK大
这又联想到[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;elset[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;
}