题目
点这里看题目。
分析
首先,解决这个问题等价于算出每个操作在什么时候会被“完全弹出”,也就是什么时候队列中不会剩下这次操作留下来的权值了。
对于 \(l=r\) 的操作:在进行完本次的操作之后,再向队列 \(l\) 中加入 \(a_l\) 个权值就会导致该操作的权值被弹出。
对于 \(l<r\) 的操作:显然,我们可以看作是该操作在 \([l,r]\) 中的每个单个队列中,被弹出的时间的最大值。问题在于,我们不可能枚举每个队列来计算,怎么办呢?
注意到,这种计算方法暗示了我们可以随意为队列分组来计算。因此,一种方法是“分块”,拆分成 \(O(\sqrt n)\) 个区间;另一种方法就是“线段树”或者“树状树组”,划分出 \(O(n)\) 个区间,并且每个操作至多覆盖 \(O(\log n)\) 个区间。
将所有的操作区间挂到线段树结点上去,并且离线按照 DFS 顺序处理所有询问。现在所有询问都变成了全局询问,“弹出”时间与“加入”时间有了单调性。因此我们可以对于某个区间上的所有询问,按照时间做一个双指针,靠后的指针维护“什么时候会被弹出”。此时我们可以专注于修改:
-
完全覆盖区间的修改:我们需要知道修改的时间,因此可以用一棵线段树(或者树状数组)维护时间。
-
部分相交区间的修改:这一部分我们又需要考虑时间、又需要考虑位置。不过根据线段树的拆分方式,部分相交的修改的总个数是 \(O(n\log n)\) 的,和线段树时间复杂度相同。因此,对于每个区间,暴力地存下这样的修改,然后加入到双指针流程中考虑即可。这一部分还需要另一棵线段树来维护序列。
这样做就是正儿八经 \(O(n\log^2n)\) 的。由于线段树反复嵌套,复杂度和运行效率并没有出现直接的关联 。
Remark.
首先简单的观察引出的关键的思考:队列互相独立、答案可以快速合并,因此我们可以将队列分组处理。当然,从一般的数据结构的角度入手也可以得到这样的结果。
草,为什么不想一想线段树?与线段树不同的地方在于,我们并没有严格遵循线段树的结构,而是将线段树看作了一种特殊的区间划分方式。这种划分方式有很好的性质:
区间个数为 \(O(n)\)。
一次修改至多经过 \(O(\log n)\) 个线段树结点(粗估一下,常数大概在 \(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;
}