「LOJ3673」简单数据结构

题目

点这里看题目。


给定一个长度为 \(n\)​ 的非负整数序列 \(a\),有 \(q\) 次操作,每次操作类型为如下三种之一:

  1. 给定 \(v\),表示 \(\forall 1\le i\le n\),令 \(a_i\gets \min\{a_i,v\}\)
  2. 表示 \(\forall 1\le i\le n\),令 \(a_i\gets a_i+i\)
  3. 给定 \(l,r\),表示你需要输出 \(\sum_{i=l}^ra_i\)

所有数据满足 \(1\le n,q\le 2\times 10^5,0\le a_i,v\le 10^{12},1\le l\le r\le n\)

分析

把玩一下这个问题,可以发现\(a\)​ 单调不降的时候,答案极其容易维护,因为 \(a\)​ 单调不降的性质在整个过程中不会受影响。具体的维护过程为:

  1. 所有的数都是 \(k(x-x_0)+y_0\) 的形式。
  2. 处理操作一:二分一个后缀,有且仅有该后缀内的数会被修改。之后将它们全部更新即可。这一部分可以结合势能用一个栈维护,这样可以将复杂度降到 \(O((n+q)\log n)\)
  3. 处理操作二:自然而然。
  4. 处理操作三:用线段树或者啥玩意儿维护一下前缀和/区间和即可。

但是初始的 \(a\) 并不一定就是单调不降的。我们很容易想到,操作中一旦出现 \(a_i\le a_{i+1}\)​​,我们就可以将两个数合并,之后一起解决。如果 \(a_i>a_{i+1}\),则\(v\le a_{i+1}\) 时,我们就可以认为两个数在操作后必然满足 \(a_{i}\le a_{i+1}\),这是一个简单的判据。

Remark.

在搞数据结构的时候,对情况的划分不一定要完全准确,但一定要方便简单

不幸的是,这样的划分并不能明显地改善复杂度。看来我们只能激进一点了——假设 \(a\) 本身就是单调不降的,然后先算一下答案,最后进行修正。一个简单的模型为,令 \(b_i=\max_{1\le j\le i}a_j\),然后用 \(b\) 替换 \(a\)

回头看看之前的判据,我们发现它惊人的适用:如果某操作一满足 \(v\le a_i\)​​​​,则在此之后都有 \(b_i=a_i\)​​(这是因为若 \(x\le y\)​​,则进行完全一样的操作后有 \(x'\le y'\)​​)。取第一次 \(v\le a_i\)​ 的操作为分界,在此之前都形如 \(kx+a_i\)​,容易维护;在此之后可以直接用单调不降的 \(b_i\)​ 替换 \(a_i\)​,也可以维护。不过,我们需要动态“加入”或“删除”一些数,最好使用线段树维护。

所以,如果找出了分界点,之后就可以做到 \(O((n+q)\log n)\)​。

找分界点很容易想到二分。如果设前 \(i\)​ 次操作的操作二个数为 \(p_i\)​,则对于 \(a_x\)​ 而言,判断 \(r\)​ 的标准为 “是否存在 \(1\le i\le r\),使得 \(v_i-p_ix\le a_x\)​?”。结构为一次式求 \(\min\),可以整体二分+凸包在 \(O((n+q)\log n)\)​ 的时间内解决。

所以,我们可以在 \(O((n+q)\log n)\) 的时间内解决本题。

代码

#include <cstdio>
#include <vector>
#include <utility>
#include <iostream>

#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 LL INF = 1e18;
const int MAXN = 2e5 + 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 Max( const _T &a, const _T &b ) {
	return a > b ? a : b;
}

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

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

int seq[MAXN], tot;

int opt[MAXN], pref[MAXN];
LL arg1[MAXN], arg2[MAXN], ans[MAXN];

LL A[MAXN], B[MAXN];

int N, Q;

namespace FindChange {
	struct Vector {
		LL x, y;

		Vector(): x( 0 ), y( 0 ) {}
		Vector( LL X, LL Y ): x( X ), y( Y ) {}

		inline Vector operator - ( const Vector &q ) const { return Vector( x - q.x, y - q.y ); }
		inline Vector operator + ( const Vector &q ) const { return Vector( x + q.x, y + q.y ); }
	};
		
	Vector stk[MAXN];

	int ord[MAXN], tmp1[MAXN], tmp2[MAXN];

	inline LL Cross( const Vector &u, const Vector &v ) {
		return u.x * v.y - u.y * v.x;
	}

	void Divide( const int &tL, const int &tR, const int &qL, const int &qR ) {
		if( tL > tR || qL > qR ) return ;
		if( tL == tR ) {
			if( tL > tot ) return ;
			rep( i, qL, qR )
				chg[seq[tL]].push_back( ord[i] );
			return ;
		}
		int h = 1, t = 0;
		int mid = ( tL + tR ) >> 1, tot1 = 0, tot2 = 0;
		for( int l = tL, r ; l <= mid ; l = r ) {
			for( r = l ; r <= mid && pref[seq[r]] == pref[seq[l]] ; r ++ );
			int idx = l;
			rep( k, l + 1, r - 1 )
				if( arg1[seq[k]] < arg1[seq[idx]] )
					idx = k;
			Vector p( pref[seq[idx]], arg1[seq[idx]] );
			while( t > 1 && Cross( stk[t] - stk[t - 1], p - stk[t] ) <= 0 ) t --;
			stk[++ t] = p;
		}
		rep( i, qL, qR ) {
			Vector d( 1, ord[i] );
			while( h < t && Cross( stk[h + 1] - stk[h], d ) >= 0 ) h ++;
			if( stk[h].y - 1ll * stk[h].x * ord[i] <= A[ord[i]] ) tmp1[++ tot1] = ord[i];
			else tmp2[++ tot2] = ord[i];
		}
		rep( i, 1, tot1 ) ord[qL + i - 1] = tmp1[i];
		rep( i, 1, tot2 ) ord[qL + tot1 + i - 1] = tmp2[i];
		Divide( tL, mid, qL, qL + tot1 - 1 );
		Divide( mid + 1, tR, qR - tot2 + 1, qR );
	}

	void Work() {
		rep( i, 1, N ) ord[i] = i;
		Divide( 1, tot + 1, 1, N );
	}
}

namespace Solve {
	struct SegTree {
		LL su[MAXN << 2], coe[MAXN << 2], tag[MAXN << 2];
	
		SegTree(): su{}, coe{}, tag{} {}
	
		inline void Cover( const int &x, const LL &nVal ) {
			su[x] = coe[x] * nVal, tag[x] = nVal;
		}
	
		inline void Normalize( const int &x ) {
			if( tag[x] == - 1e9 ) return ;
			Cover( x << 1, tag[x] );
			Cover( x << 1 | 1, tag[x] );
			tag[x] = - 1e9;
		}
	
		void ModifyCoe( const int &x, const int &l, const int &r, const int &p, const LL &nVal ) {
			if( l == r ) { coe[x] = nVal; return ; }
			int mid = ( l + r ) >> 1; Normalize( x );
			if( p <= mid ) ModifyCoe( x << 1, l, mid, p, nVal );
			else ModifyCoe( x << 1 | 1, mid + 1, r, p, nVal );
			coe[x] = coe[x << 1] + coe[x << 1 | 1];
		}
	
		void Cover( const int &x, const int &l, const int &r, const int &segL, const int &segR, const LL &nVal ) {
			if( segL <= l && r <= segR ) { Cover( x, nVal ); return ; }
			int mid = ( l + r ) >> 1; Normalize( x );
			if( segL <= mid ) Cover( x << 1, l, mid, segL, segR, nVal );
			if( mid  < segR ) Cover( x << 1 | 1, mid + 1, r, segL, segR, nVal );
			su[x] = su[x << 1] + su[x << 1 | 1];
		}
		
		LL Query( const int &x, const int &l, const int &r, const int &segL, const int &segR, const int &arg ) {
			if( segL <= l && r <= segR ) return coe[x] * arg + su[x];
			int mid = ( l + r ) >> 1; LL ret = 0; Normalize( x );
			if( segL <= mid ) ret += Query( x << 1, l, mid, segL, segR, arg );
			if( mid  < segR ) ret += Query( x << 1 | 1, mid + 1, r, segL, segR, arg );
			return ret;
		}
	
		void Build( const int &x, const int &l, const int &r, const LL *a, const int &typ ) {
			if( l > r ) return ;
			tag[x] = - 1e9;
			if( l == r ) {
				su[x] = a == NULL ? 0 : a[l];
				coe[x] = typ == 0 ? 0 : ( typ == 1 ? 1 : l );
				return ;
			}
			int mid = ( l + r ) >> 1;
			Build( x << 1, l, mid, a, typ );
			Build( x << 1 | 1, mid + 1, r, a, typ );
			su[x] = su[x << 1] + su[x << 1 | 1];
			coe[x] = coe[x << 1] + coe[x << 1 | 1];
		}
	};

	struct Node {
		int l, r; LL x0, y0;
	
		Node(): l( 0 ), r( 0 ), x0( 0 ), y0( 0 ) {}
		Node( int L, int R, LL X, LL Y ): l( L ), r( R ), x0( X ), y0( Y ) {}
	};

	SegTree tre[2];
	Node stk[MAXN]; int top = 0;

	void Work() {
		tre[0].Build( 1, 1, N, A, 1 );
		tre[1].Build( 1, 1, N, NULL, 2 );
		rep( i, 1, Q ) {
			if( opt[i] == 1 ) 
				for( const int &x : chg[i] )
					tre[0].Cover( 1, 1, N, x, x, 0 ),
					tre[1].ModifyCoe( 1, 1, N, x, 0 );
			if( opt[i] == 3 ) {
				int l = arg1[i], r = arg2[i];
				ans[i] += tre[1].Query( 1, 1, N, l, r, pref[i] ) + tre[0].Query( 1, 1, N, l, r, 0 );
			}
		}
		tre[0].Build( 1, 1, N, NULL, 0 );
		tre[1].Build( 1, 1, N, NULL, 0 );
		rep( i, 1, N ) stk[++ top] = Node( i, i, 0, B[i] );
		rep( i, 1, Q ) {
			if( opt[i] == 1 ) {
				for( const int &x : chg[i] ) 
					tre[0].ModifyCoe( 1, 1, N, x, +1 ),
					tre[1].ModifyCoe( 1, 1, N, x, +x );
				LL v = arg1[i];
				if( stk[top].y0 + 1ll * N * ( pref[i] - stk[top].x0 ) < v ) continue;
				while( top && stk[top].y0 + 1ll * stk[top].l * ( pref[i] - stk[top].x0 ) >= v ) top --;
				if( top && stk[top].y0 + 1ll * stk[top].r * ( pref[i] - stk[top].x0 ) >= v ) {
					int l = stk[top].l, r = stk[top].r, mid;
					while( l < r ) {
						mid = ( l + r ) >> 1;
						if( stk[top].y0 + 1ll * mid * ( pref[i] - stk[top].x0 ) >= v ) r = mid;
						else l = mid + 1;
					}
					stk[top].r = l - 1;
					stk[++ top] = Node( l, N, pref[i], v );
					tre[0].Cover( 1, 1, N, l, N, v );
					tre[1].Cover( 1, 1, N, l, N, - pref[i] );
				} else {
					top ++, stk[top] = Node( stk[top - 1].r + 1, N, pref[i], v );
					tre[0].Cover( 1, 1, N, stk[top].l, N, v );
					tre[1].Cover( 1, 1, N, stk[top].l, N, - pref[i] );
				}
			}
			if( opt[i] == 3 ) {
				int l = arg1[i], r = arg2[i];
				ans[i] += tre[1].Query( 1, 1, N, l, r, pref[i] ) + tre[0].Query( 1, 1, N, l, r, 0 );
			}
		}
	}
}

int main() {
	// freopen( "ds.in", "r", stdin );
	// freopen( "ds.out", "w", stdout );
	Read( N ), Read( Q );
	rep( i, 1, N ) Read( A[i] ), B[i] = Max( B[i - 1], A[i] );
	rep( i, 1, Q ) {
		Read( opt[i] );
		if( opt[i] == 1 ) Read( arg1[i] );
		if( opt[i] == 3 ) Read( arg1[i] ), Read( arg2[i] );
		pref[i] = pref[i - 1] + ( opt[i] == 2 );
		if( opt[i] == 1 ) seq[++ tot] = i;
	}
	FindChange :: Work();
	Solve :: Work();
	rep( i, 1, Q ) if( opt[i] == 3 ) Write( ans[i] ), putchar( '\n' );
	return 0;
}
posted @ 2023-01-26 22:15  crashed  阅读(40)  评论(0编辑  收藏  举报