「LOJ2461」完美的队列

题目

点这里看题目。

分析

首先,解决这个问题等价于算出每个操作在什么时候会被“完全弹出”,也就是什么时候队列中不会剩下这次操作留下来的权值了。

对于 \(l=r\) 的操作:在进行完本次的操作之后,再向队列 \(l\) 中加入 \(a_l\) 个权值就会导致该操作的权值被弹出。

对于 \(l<r\) 的操作:显然,我们可以看作是该操作在 \([l,r]\) 中的每个单个队列中,被弹出的时间的最大值。问题在于,我们不可能枚举每个队列来计算,怎么办呢?

注意到,这种计算方法暗示了我们可以随意为队列分组来计算。因此,一种方法是“分块”,拆分成 \(O(\sqrt n)\) 个区间;另一种方法就是“线段树”或者“树状树组”,划分出 \(O(n)\) 个区间,并且每个操作至多覆盖 \(O(\log n)\) 个区间。

将所有的操作区间挂到线段树结点上去,并且离线按照 DFS 顺序处理所有询问。现在所有询问都变成了全局询问,“弹出”时间与“加入”时间有了单调性。因此我们可以对于某个区间上的所有询问,按照时间做一个双指针,靠后的指针维护“什么时候会被弹出”。此时我们可以专注于修改:

  1. 完全覆盖区间的修改:我们需要知道修改的时间,因此可以用一棵线段树(或者树状数组)维护时间。

  2. 部分相交区间的修改:这一部分我们又需要考虑时间、又需要考虑位置。不过根据线段树的拆分方式,部分相交的修改的总个数是 \(O(n\log n)\),和线段树时间复杂度相同。因此,对于每个区间,暴力地存下这样的修改,然后加入到双指针流程中考虑即可。这一部分还需要另一棵线段树来维护序列。

这样做就是正儿八经 \(O(n\log^2n)\) 的。由于线段树反复嵌套,复杂度和运行效率并没有出现直接的关联 😓。

Remark.

首先简单的观察引出的关键的思考:队列互相独立、答案可以快速合并,因此我们可以将队列分组处理。当然,从一般的数据结构的角度入手也可以得到这样的结果。

草,为什么不想一想线段树?

与线段树不同的地方在于,我们并没有严格遵循线段树的结构,而是将线段树看作了一种特殊的区间划分方式。这种划分方式有很好的性质:

  1. 区间个数为 \(O(n)\)

  2. 一次修改至多经过 \(O(\log n)\) 个线段树结点(粗估一下,常数大概在 \(4\) 左右)。

  3. 区间虽然会有重叠,但是依照树形结构,区间之间要么包含要么不交。

  4. 挂到线段树结点上的询问可以看作区间上的全局询问。

现在,我们可以逐区间处理。并且,存在包含关系的区间往往会有影响,对着树形 DFS 可以帮助我们处理这种影响。

代码

#include <cstdio>
#include <vector>

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

const int MAXN = 1e5 + 5;

template<typename _T>
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>
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 Max( const _T &a, const _T &b ) {
	return a > b ? a : b;
}

template<typename _T>
inline _T Abs( const _T &a ) {
	return a < 0 ? -a : a;
}

std :: vector<int> opt[MAXN];

int app[MAXN], ans = 0;

int qL[MAXN], qR[MAXN], qX[MAXN], ddl[MAXN];
int A[MAXN];

int N, M;

namespace Time {
	int su[MAXN];

	inline void Down( int &x ) { x &= x - 1; }
	inline void Up( int &x ) { x += x & ( - x ); }
	inline void Update( int x, int v ) { for( ; x <= M ; Up( x ) ) su[x] += v; }
	inline  int Query( int x ) { int ret = 0; for( ; x ; Down( x ) ) ret += su[x]; return ret; }
	inline  int Query( const int &l, const int &r ) { return Query( r ) - Query( l - 1 ); }

	inline int Search( const int &lim ) {
		int p = 0, val = 0;
		for( int k = 16 ; ~ k ; k -- )
			if( p + ( 1 << k ) <= M && val + su[p | 1 << k] < lim )
				val += su[p |= 1 << k];
		return p + 1;
	}
}

namespace Sequence {
	int mx[MAXN << 2], tag[MAXN << 2];

	inline void Upt( const int &x ) {
		mx[x] = Max( mx[x << 1], mx[x << 1 | 1] );
	}

	inline void Add( const int &x, const int &delt ) {
		mx[x] += delt, tag[x] += delt;
	}

	inline void Normalize( const int &x ) {
		if( ! tag[x] ) return ;
		Add( x << 1, tag[x] );
		Add( x << 1 | 1, tag[x] );
		tag[x] = 0;
	}

	void Build( const int &x, const int &l, const int &r ) {
		if( l > r ) return ;
		if( l == r ) { mx[x] = A[l]; return ; }
		int mid = ( l + r ) >> 1;
		Build( x << 1, l, mid );
		Build( x << 1 | 1, mid + 1, r );
		Upt( x );
	}

	void Modify( const int &x, const int &l, const int &r, const int &segL, const int &segR, const int &delt ) {
		if( segL <= l && r <= segR ) { Add( x, delt ); return ; }
		int mid = ( l + r ) >> 1; Normalize( x );
		if( segL <= mid ) Modify( x << 1, l, mid, segL, segR, delt );
		if( mid  < segR ) Modify( x << 1 | 1, mid + 1, r, segL, segR, delt );
		Upt( x );
	}

	int Query( const int &x, const int &l, const int &r, const int &segL, const int &segR ) {
		if( segL <= l && r <= segR ) return mx[x];
		int mid = ( l + r ) >> 1; Normalize( x );
		if( segR <= mid ) return Query( x << 1, l, mid, segL, segR );
		if( mid  < segL ) return Query( x << 1 | 1, mid + 1, r, segL, segR );
		return Max( Query( x << 1, l, mid, segL, segR ), Query( x << 1 | 1, mid + 1, r, segL, segR ) );
	}

	inline int Query( const int &l, const int &r ) {
		return Query( 1, 1, N, l, r );
	}

	inline void Modify( const int &segL, const int &segR, const int &delt ) {
		Modify( 1, 1, N, segL, segR, delt );
	}
}

namespace GetDDL {
	std :: vector<int> mdf[MAXN << 2];
	bool any[MAXN << 2];

	void Hang( const int &x, const int &l, const int &r, const int &segL, const int &segR, const int &qId ) {
		if( segL <= l && r <= segR ) {
			mdf[x].push_back( qId ), any[x] = true;
			return ;
		}
		int mid = ( l + r ) >> 1; mdf[x].push_back( - qId );
		if( segL <= mid ) Hang( x << 1, l, mid, segL, segR, qId );
		if( mid  < segR ) Hang( x << 1 | 1, mid + 1, r, segL, segR, qId );
	}
	
	void DFS( const int &u, const int &l, const int &r ) {
		if( mdf[u].empty() ) return ;
		int n = mdf[u].size();
		if( any[u] ) {
			int p = 0, q = 0;
			for( ; p < n ; p ++ ) {
				int tL = Abs( mdf[u][p] );
				Sequence :: Modify( qL[tL], qR[tL], +1 );
				if( mdf[u][p] > 0 ) {
					bool flg = false;
					int tR, val, pre = Time :: Query( tL - 1 );
					while( q < p ) {
						tR = Abs( mdf[u][q] );
						Sequence :: Modify( qL[tR], qR[tR], -1 ), q ++;
					}
					while( q < n ) {
						tR = Abs( mdf[u][q] );
						val = Sequence :: Query( l, r ) + pre;
						if( val <= Time :: Query( tR ) ) {
							ddl[tL] = Max( ddl[tL], Time :: Search( val ) );
							flg = true;
							break;
						}
						Sequence :: Modify( qL[tR], qR[tR], -1 ), q ++;
						if( Sequence :: Query( l, r )  + pre <= Time :: Query( tR ) ) {
							ddl[tL] = Max( ddl[tL], tR ), flg = true;
							break;
						}
					}
					if( ! flg && q == n ) {
						val = Sequence :: Query( l, r ) + pre;
						if( val <= Time :: Query( M ) )
							ddl[tL] = Max( ddl[tL], Time :: Search( val ) );
						else ddl[tL] = M + 1;
					}
				}
			}
			while( q < n ) {
				int tR = Abs( mdf[u][q] );
				Sequence :: Modify( qL[tR], qR[tR], -1 ), q ++;
			}
		}
		if( l == r ) return ;
		int mid = ( l + r ) >> 1;
		for( int i = 0 ; i < n ; i ++ ) if( mdf[u][i] > 0 ) 
			Time :: Update( mdf[u][i], +1 );
		DFS( u << 1, l, mid );
		DFS( u << 1 | 1, mid + 1, r );
		for( int i = 0 ; i < n ; i ++ ) if( mdf[u][i] > 0 ) 
			Time :: Update( mdf[u][i], -1 );
	}

	void Work() {
		Sequence :: Build( 1, 1, N );
		DFS( 1, 1, N );
	}
}

int main() {
	read( N ), read( M );
	rep( i, 1, N ) read( A[i] );
	rep( i, 1, M ) {
		read( qL[i] ), read( qR[i] ), read( qX[i] );
		GetDDL :: Hang( 1, 1, N, qL[i], qR[i], i );
	}
	GetDDL :: Work();
	rep( i, 1, M ) {
		opt[ddl[i]].push_back( qX[i] );
		int n = opt[i].size();
		rep( j, 0, n - 1 )
			ans -= ! ( -- app[opt[i][j]] );
		ans += ! ( app[qX[i]] ++ );
		write( ans ), putchar( '\n' );
	}
	return 0;
}
posted @ 2022-07-13 09:33  crashed  阅读(51)  评论(0编辑  收藏  举报