「雅礼集训 2017 Day1」市场

题目

点这里看题目。

分析

看起来其它操作都易于维护,唯独这个区间下取整除的修改非常不可做。唯一的办法似乎是暴力地修改每一个节点。

但是我们可以挑出一些特殊情况来特殊处理。比如,考虑一个最大值为 (a),最小值为 (b) 的区间。如果对于 (d) 求下取整除的时候,出现了 (lfloorfrac a d floor=lfloorfrac b d floor),那么这个时候修改相当于区间赋值,可以打标记;如果出现了 (a-lfloorfrac a d floor=b-lfloorfrac b d floor),那么就相当于区间加,可以打标记。

然后加上两个剪枝......就可以通过了?!看起来跑得很快,下面我们需要证明复杂度。


先来考虑两种特判。对于 (lfloorfrac a d floor=lfloorfrac b d floor),它的一条充分条件为 (a=b);而对于 (a-lfloorfrac a d floor=b-lfloorfrac b d floor),如果此时有 (lfloorfrac a d floor ot=lfloorfrac b d floor),那么一定会有 (lfloorfrac a d floor=lfloorfrac b d floor+1)(a=b+1)

因此,对于 (a-ble 1) 的区间,由于可以直接标记处理,因此它不会贡献暴力修改的复杂度。而如果有 (a-b=cge 2),那么可以得到 (lfloorfrac a d floor-lfloorfrac b d floorle lceilfrac c d ceil),这个区间最多被暴力修改 (O(log c)) 次。因此,设某个结点 (x) 的区间内最大值为 (a_x),最小值为 (b_x),则它的势能应该被定义为 (phi(x)=log(max{1,a_x-b_x})),整棵树的势能为 (Phi=sum_xphi(x)=sum_xlog(max{1,a_x-b_x}))

通过势能来分析复杂度:

  1. 初始时:势能为 (O(nlog|V|)),其中 (V) 为值域。

  2. 区间加:对于那些被完全覆盖的结点,势能不变。而对于那些仅和修改区间有交、但没被覆盖的结点,由线段树的复杂度分析可知,这些结点数量为 (O(log n)),每个结点的势能变化量为 (O(log |C|)),其中 (C) 为修改的值的值域。

    这部分的 (Delta_{Phi}=O(log nlog |C|))

  3. 区间下取整除:这一部分有基础复杂度 (O(log n)),属于没有被区间完全覆盖的那些结点的遍历复杂度。

    而考虑一个被暴力修改的结点 (x),一旦它被包含、且递归进入修改,那么 (Delta_{phi(x)}<0)。由于势能不会超过 (O(nlog|V|+qlog nlog|C|)),因此这样的递归修改只会进行不超过 (O(nlog|V|+qlog nlog|C|)) 次,单次修改 (O(1))

因此,该算法的复杂度为 (O(qlog nlog|C|+nlog |V|))

小结:

  1. 特别注意势能分析的关键:定义势函数和分析势能变化。势函数的定义需要关注算法中决定复杂度的关键量,而分析势能变化则需要分析哪些应该被放入势能,而哪些应该被直接算入复杂度
  2. 特别注意势能线段树的实现:关注一下分析的边界情况,比如本题中,如果不处理 (a-b=1) 的情况,那么会出现 (lceilfrac{1}{d} ceil=1) 的情况,导致势能没法下降;此外,如果不能得到较好的分析,那么在保证正确的前提下可以尽量多加一点优化

代码

#include <cmath>
#include <cstdio>
#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 int INF = 0x3f3f3f3f;
const int MAXN = 1e5 + 5;

template<typename _T>
void read( _T &x )
{
	x = 0; char s = getchar(); int f = 1;
	while( ! ( '0' <= s && s <= '9' ) ) { f = 1; if( s == '-' ) f = -1; s = getchar(); }
	while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
	x *= f;
}

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

LL su[MAXN << 2];
int mx[MAXN << 2], mn[MAXN << 2];
int tagCov[MAXN << 2], tagAdd[MAXN << 2];

int A[MAXN];
int N, Q;

inline int Div( const int a, const int b ) { return ( int ) floor( 1.0 * a / b ); }

inline void Upt( const int x )
{
	su[x] = su[x << 1] + su[x << 1 | 1];
	mx[x] = std :: max( mx[x << 1], mx[x << 1 | 1] );
	mn[x] = std :: min( mn[x << 1], mn[x << 1 | 1] );
}

inline void Add( const int x, const int l, const int r, const int delt )
{
	su[x] += 1ll * ( r - l + 1 ) * delt;
	mx[x] += delt, mn[x] += delt, tagAdd[x] += delt;
}

inline void Cover( const int x, const int l, const int r, const int v )
{
	su[x] = 1ll * ( r - l + 1 ) * v;
	mx[x] = mn[x] = tagCov[x] = v, tagAdd[x] = 0;
}

inline void Normalize( const int x, const int l, const int r )
{
	int mid = ( l + r ) >> 1;
	if( tagCov[x] != - INF )
	{
		Cover( x << 1, l, mid, tagCov[x] );
		Cover( x << 1 | 1, mid + 1, r, tagCov[x] );
		tagCov[x] = - INF;
	}
	if( tagAdd[x] )
	{
		Add( x << 1, l, mid, tagAdd[x] );
		Add( x << 1 | 1, mid + 1, r, tagAdd[x] );
		tagAdd[x] = 0;
	}
}

void Build( const int x, const int l, const int r )
{
	if( l > r ) return ;
	tagCov[x] = - INF, tagAdd[x] = 0;
	if( l == r ) { su[x] = mx[x] = mn[x] = A[l]; return ; }
	int mid = ( l + r ) >> 1;
	Build( x << 1, l, mid );
	Build( x << 1 | 1, mid + 1, r );
	Upt( x );
}

void OrderAdd( 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, l, r, delt ); return ; }
	int mid = ( l + r ) >> 1; Normalize( x, l, r );
	if( segL <= mid ) OrderAdd( x << 1, l, mid, segL, segR, delt );
	if( mid  < segR ) OrderAdd( x << 1 | 1, mid + 1, r, segL, segR, delt );
	Upt( x );
}

void Div( const int x, const int l, const int r, const int d )
{
	int t1 = Div( mx[x], d ), t2 = Div( mn[x], d );
	if( t1 == t2 ) { Cover( x, l, r, t1 ); return ; }
	if( t1 - mx[x] == t2 - mn[x] ) { Add( x, l, r, t1 - mx[x] ); return ; }
	if( l == r )
	{
		su[x] = mx[x] = mn[x] = Div( mx[x], d );
		return ;
	}
	int mid = ( l + r ) >> 1; Normalize( x, l, r );
	Div( x << 1, l, mid, d );
	Div( x << 1 | 1, mid + 1, r, d );
	Upt( x );
}

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

LL QuerySum( const int x, const int l, const int r, const int segL, const int segR )
{
	if( segL <= l && r <= segR ) return su[x];
	int mid = ( l + r ) >> 1; LL ret = 0; Normalize( x, l, r );
	if( segL <= mid ) ret += QuerySum( x << 1, l, mid, segL, segR );
	if( mid  < segR ) ret += QuerySum( x << 1 | 1, mid + 1, r, segL, segR );
	return ret;
}

int QueryMin( const int x, const int l, const int r, const int segL, const int segR )
{
	if( segL <= l && r <= segR ) return mn[x];
	int mid = ( l + r ) >> 1, ret = INF; Normalize( x, l, r );
	if( segL <= mid ) ret = std :: min( ret, QueryMin( x << 1, l, mid, segL, segR ) );
	if( mid  < segR ) ret = std :: min( ret, QueryMin( x << 1 | 1, mid + 1, r, segL, segR ) );
	return ret;
}

int main()
{
	read( N ), read( Q );
	rep( i, 1, N ) read( A[i] );
	Build( 1, 1, N );
	int opt, A, B, C;
	while( Q -- )
	{
		read( opt ), read( A ), read( B ), A ++, B ++;
		if( opt == 1 ) read( C ), 
			OrderAdd( 1, 1, N, A, B, C );
		if( opt == 2 ) read( C ), 
			OrderDiv( 1, 1, N, A, B, C );
		if( opt == 3 ) write( QueryMin( 1, 1, N, A, B ) ), putchar( '
' );
		if( opt == 4 ) write( QuerySum( 1, 1, N, A, B ) ), putchar( '
' );
	}
	return 0;
}
原文地址:https://www.cnblogs.com/crashed/p/15177850.html