zoukankan      html  css  js  c++  java
  • 「雅礼集训 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;
    }
    
  • 相关阅读:
    HTML <iframe> 标签
    HTML <tr> 标签
    HTML <img> 标签的 border 属性
    jQuery ajax ajax() 方法
    CSS padding 属性
    SQL Server CONVERT() 函数
    CSS overflow 属性
    HTML <input> 标签
    Java动态代理一——动态类Proxy的使用
    Java拆箱装箱小结
  • 原文地址:https://www.cnblogs.com/crashed/p/15177850.html
Copyright © 2011-2022 走看看