「UOJ749」稳健型选手

题目

点这里看题目。

分析

首先考虑 \(q=1,l=1,r=n\) 的情况。我们设 QAQ 取了 \(k\) 张牌,牌的编号分别为 \(b_1,b_2,\dots,b_k\),不难发现判定合法性的直接条件:

一种选法 \(b_1,b_2,\dots,b_k\) 是合法的,当且仅当 \(\forall 1\le j\le k,b_j\ge 2j-1\)

这样,\(k\) 的最大值就是 \(\left\lceil\frac{n}{2}\right\rceil\),记这个值为 \(m\)。我们将上述条件等价转化一下,其实可以得到一个更好用的条件:

一种选法 \(b_1,b_2,\dots,b_k\) 是合法的,当且仅当 \(\forall 1\le j\le k\),区间 \([2j-1,n]\) 中被选中的牌 \(\le n-j\)

这可以直接导出一种 \(O(n\log n)\) 的做法:维护备选集合 \(S\)。我们按照 \(j\) 从大到小的顺序依次考虑每个区间 \([2j-1,n]\),每次将相较于 \([2j+1,n]\) 新增的牌加入到 \(S\) 中,而后取出 \(S\) 中权值最大的牌、从 \(S\) 中删除并将它的权值算入答案。

Remark.

顺便一说,这个问题实际上就是「NOI2017」蔬菜的弱化。所以堆贪心的思路是共通的。

回到区间询问上来。首先,我们可以假设 \(l,r\) 的奇偶性不同,如果相同的话 \(r\) 这张牌一定可以取到。则上述贪心过程可以很容易地从 \([l,r]\) 扩展到 \([l-2,r]\),如果我们还能实现从 \([l,r]\)\([l,r+2]\) 的扩展,那么我们就可以实现一个回滚莫队。

考虑这样修改的影响:我们一开始会先处理 \(r+1,r+2\) 这两张牌,将权值较大的一张记入答案,并将权值较小的一张留在 \(S\) 里面,然后进行 \([l,r]\) 的贪心。如果我们得到了 \([l,r]\) 贪心选择的牌集合 \(B\),那么加入了一张牌对于 \(B\) 的影响,实际上就是先将牌加入到 \(B\) 当中,再将 \(B\) 中最小的一张牌删掉——因为在最终结果中,\(B\) 中的牌和新加入的牌都是可以任意保留的。

Remark.

考场上,我似乎先入为主地以为扩展到 \([l,r+2]\) 的过程是不可实现的 😢。

“一次做一步扩展”可以很容易地修改成“一次做多步扩展”。直接利用上述思想,如果我们想要从 \([l,r]\) 扩展到 \([l,r+2k]\),那么我们可以先取出 \([r+1,r+2],[r+3,r+4],\dots,[r+2k-1,r+2k]\) 各个区间的较大值记入答案,将各个区间的较小值和 \([l,r]\) 贪心的结果 \(B\) 合并——也即选取这些牌中的前 \(\frac{r-l+1}{2}\) 大——就得到了答案。

怎么用好这样的操作?“一次做多步扩展”其实就是说“区间信息可以二合一”,可以二合一的区间信息处理可以使用分治。模仿上面的合并即可,查询前 \(\frac{r-l+1}{2}\) 大可以使用主席树。复杂度即为 \(O(n\log^2n+q\log n)\)

代码

#include <queue>
#include <cstdio>
#include <vector>
#include <utility>
#include <algorithm>

#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )

typedef long long LL;

const int MAXN = 2e5 + 5, MAXS = 5e6 + 5;

template<typename _T>
inline void Read( _T &x ) {
	x = 0; char s = getchar(); bool f = false;
	while( ! ( '0' <= s && s <= '9' ) ) { f = s == '-', s = getchar(); }
	while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s ^ '0' ), s = getchar(); }
	if( f ) x = -x;
}

template<typename _T>
inline void Write( _T x ) {
	if( x < 0 ) putchar( '-' ), x = -x;
	if( 9 < x ) Write( x / 10 );
	putchar( x % 10 + '0' );
}

template<typename _T>
inline _T Min( const _T &a, const _T &b ) {
	return a < b ? a : b;
}

template<typename _T>
inline _T Max( const _T &a, const _T &b ) {
	return a > b ? a : b;
}

std :: vector<int> qry[MAXN], beg[2];

int qL[MAXN], qR[MAXN];
LL ans[MAXN];

std :: priority_queue<int, std :: vector<int>, std :: less<int> > lRt;
std :: priority_queue<int, std :: vector<int>, std :: greater<int> > sRt;

LL pref[MAXN];
int siz[MAXS]; LL su[MAXS];
int lch[MAXS], rch[MAXS];
int rt[MAXN], ntot;

int A[MAXN];
int val[MAXN], tot;

int N, Q;

inline void Discrete() {
	rep( i, 1, N ) val[++ tot] = A[i];
	std :: sort( val + 1, val + 1 + tot );
	tot = std :: unique( val + 1, val + 1 + tot ) - val - 1;
	rep( i, 1, N ) A[i] = std :: lower_bound( val + 1, val + 1 + tot, A[i] ) - val;
}

inline int NewNode() {
	static int r;

	r = ++ ntot;
	lch[r] = rch[r] = siz[r] = su[r] = 0;
	return r;
}

inline void Copy( const int &a, const int &b ) {
	lch[a] = lch[b], rch[a] = rch[b], siz[a] = siz[b], su[a] = su[b];
}

inline void Upt( const int &x ) {
	su[x] = su[lch[x]] + su[rch[x]];
	siz[x] = siz[lch[x]] + siz[rch[x]];
}

int ConsUpdate( const int &x, const int &l, const int &r, const int &p, const int &delt ) {
	int cur = NewNode(); Copy( cur, x );
	if( l == r ) siz[cur] += delt, su[cur] += val[p] * delt;
	else {
		int mid = ( l + r ) >> 1;
		if( p <= mid ) lch[cur] = ConsUpdate( lch[x], l, mid, p, delt );
		else rch[cur] = ConsUpdate( rch[x], mid + 1, r, p, delt );
		Upt( cur );
	}
	return cur;
}

void DynaUpdate( int &x, const int &l, const int &r, const int &p, const int &delt ) {
	x = x ? x : NewNode();
	if( l == r ) { siz[x] += delt, su[x] += delt * val[p]; return ; }
	int mid = ( l + r ) >> 1;
	if( p <= mid ) DynaUpdate( lch[x], l, mid, p, delt );
	else DynaUpdate( rch[x], mid + 1, r, p, delt );
	Upt( x ); 
}

LL Search( const int &x, const int &y, const int &l, const int &r, const int &rnk ) {
	if( ! x && ! y ) return 0;
	if( l == r ) return 1ll * rnk * val[l];
	int mid = ( l + r ) >> 1;
	if( rnk <= siz[rch[x]] + siz[rch[y]] )
		return Search( rch[x], rch[y], mid + 1, r, rnk );
	return Search( lch[x], lch[y], l, mid, rnk - siz[rch[x]] - siz[rch[y]] ) + su[rch[x]] + su[rch[y]];
}

void Divide( const int &l, const int &r, const std :: vector<int> &rng ) {
	if( r <= l + 1 || rng.empty() ) return ;
	std :: vector<int> lef, rig, crs;
	int mid = ( ( l - 1 ) / 2 + ( r - 1 ) / 2 + 2 ) / 2 * 2 - ( r & 1 ), n = rng.size();
	for( int i = 0 ; i < n ; i ++ ) {
		int x = rng[i];
		if( qL[x] <= mid && mid < qR[x] ) crs.push_back( x );
		else {
			if( qR[x] <= mid ) lef.push_back( x );
			if( mid  < qL[x] ) rig.push_back( x );
		}
	}
	Divide( l, mid, lef );
	Divide( mid + 1, r, rig );
	n = crs.size();
	for( int i = l ; i <= mid ; i += 2 ) qry[i].clear();
	for( int i = 0 ; i < n ; i ++ ) qry[qL[crs[i]]].push_back( crs[i] );
	ntot = 0; 
	while( ! sRt.empty() ) sRt.pop();
	for( int i = mid + 1 ; i <= r ; i += 2 ) {
		rt[i + 1] = i == mid + 1 ? 0 : rt[i - 1];
		pref[i + 1] = i == mid + 1 ? 0 : pref[i - 1];
		sRt.push( A[i] ), pref[i + 1] += val[A[i]]; 
		sRt.push( A[i + 1] ), pref[i + 1] += val[A[i + 1]];
		rt[i + 1] = ConsUpdate( rt[i + 1], 1, tot, sRt.top(), +1 );
		pref[i + 1] -= val[sRt.top()], sRt.pop();
	}
	int curRt = 0;
	while( ! lRt.empty() ) lRt.pop();
	for( int i = mid - 1 ; i >= l ; i -= 2 ) {
		lRt.push( A[i] ), lRt.push( A[i + 1] );
		int h = lRt.top(); lRt.pop();
		DynaUpdate( curRt, 1, tot, h, +1 );
		int m = qry[i].size();
		for( int j = 0 ; j < m ; j ++ ) {
			int x = qry[i][j];
			ans[x] += Search( curRt, rt[qR[x]], 1, tot, ( mid - qL[x] + 1 ) >> 1 ) + pref[qR[x]];
		}
	}
}

int main() {
	Read( N ), Read( Q );
	rep( i, 1, N ) Read( A[i] );
	Discrete();
	rep( i, 1, Q ) {
		Read( qL[i] ), Read( qR[i] );
		if( ( qR[i] - qL[i] + 1 ) & 1 )
			ans[i] += val[A[qR[i] --]];
		if( qL[i] <= qR[i] ) {
			if( qR[i] == qL[i] + 1 )
				ans[i] += Max( val[A[qL[i]]], val[A[qR[i]]] );
			else
				beg[qL[i] % 2].push_back( i );
		}
	}
	Divide( 1, N - ( N & 1 ), beg[1] );
	Divide( 2, N - 1 + ( N & 1 ), beg[0] );
	rep( i, 1, Q ) Write( ans[i] ), putchar( '\n' );
	return 0;
}
posted @ 2022-08-10 14:58  crashed  阅读(71)  评论(0编辑  收藏  举报