1. 程式人生 > 其它 >Solution -「LOJ #6029」「雅禮集訓 2017」市場

Solution -「LOJ #6029」「雅禮集訓 2017」市場

\(\mathcal{Description}\)

  Link.

  維護序列 \(\lang a_n\rang\),支援 \(q\) 次如下操作:

  1. 區間加法;

  2. 區間下取整除法;

  3. 區間求最小值;

  4. 區間求和。

    \(n,q\le10^5\),值域大約是 \(V=2\times10^9\)

\(\mathcal{Solution}\)

  可以推測是勢能線段樹。對於線段樹上的區間 \([l,r]\),想要將它 \(\div d\),維護 \(u=\min_{i=l}^r\{a_i\}\) 以及 \(v=\max_{i=l}^r\{a_i\}\),當 \(\lfloor\frac{u}{d}\rfloor-u=\lfloor\frac{v}{d}\rfloor-v\)

時直接打區間加法標記,否則遞迴處理。其他操作正常線上段樹上進行,若不考慮各變數間的數量級關係,可證複雜度為 \(\mathcal O((n+q\log n)\log V)\)

證明   設線段樹上區間 $[l,r]$ 有勢能 $\Phi[l,r]=\log(v-u)$($v,u$ 定義如上),總勢能 $\Phi=\sum\Phi[l,r]$,則:
  • 初始狀態,\(\Phi[l,r]\le\mathcal O(\log V)\)\(\Phi\le\mathcal O(n\log V)\)
  • 區間加法,勢能有變動的區間個數為 \(\mathcal O(\log n)\)\(\Delta\Phi[l,r]\le\mathcal O(\log V)\)
    ,故 \(\Delta\Phi=\sum\Delta\Phi[l,r]\le\mathcal O(\log n\log V)\)
  • 區間除法,設操作物件為區間 \([s,e]\),首先其有基礎複雜度開銷 \(\mathcal O(\log n)\),也有可能帶來至多 \(\mathcal O(\log n\log V)\) 的勢能增加。考慮樹上一個被它完全覆蓋的區間 \([l,r]\)。若在這個區間需要向下遞迴,則必然有 \(\Delta\Phi[l,r]\le-1\),所以 \(\Delta\Phi\propto x\),其中 \(x\) 即遞迴入完全覆蓋區間的次數。那麼本次操作的複雜度為 \(\mathcal O(\log n+x)\)

  綜上,總複雜度為 \(\mathcal O(q\log n)+\sum x\),即 \(\mathcal O(q\log n+n\log V+q\log n\log V)\),當然第一項可以忽略。 \(\square\)

\(\mathcal{Code}\)

/*~Rainybunny~*/

#include <cmath>
#include <cstdio>

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

typedef long long LL;
#define int LL

inline int rint() {
    int x = 0, f = 1, s = getchar();
    for ( ; s < '0' || '9' < s; s = getchar() ) f = s == '-' ? -f : f;
    for ( ; '0' <= s && s <= '9'; s = getchar() ) x = x * 10 + ( s ^ '0' );
    return x * f;
}

template<typename Tp>
inline void wint( Tp x ) {
    if ( x < 0 ) putchar( '-' ), x = -x;
    if ( 9 < x ) wint( x / 10 );
    putchar( x % 10 ^ '0' );
}

inline int imin( const int a, const int b ) { return a < b ? a : b; }
inline int imax( const int a, const int b ) { return a < b ? b : a; }

const int MAXN = 1e5, IINF = 1ll << 60;
int n, q;

struct SegmentTree {
    int len[MAXN << 2], mx[MAXN << 2], mn[MAXN << 2], adt[MAXN << 2];
    LL sum[MAXN << 2];

    inline void pushup( const int u ) {
        mx[u] = imax( mx[u << 1], mx[u << 1 | 1] );
        mn[u] = imin( mn[u << 1], mn[u << 1 | 1] );
        sum[u] = sum[u << 1] + sum[u << 1 | 1];
    }


    inline void pushad( const int u, const int v ) {
        adt[u] += v, mn[u] += v, mx[u] += v, sum[u] += 1ll * v * len[u];
    }

    inline void pushdn( const int u ) {
        if ( adt[u] ) {
            pushad( u << 1, adt[u] ), pushad( u << 1 | 1, adt[u] );
            adt[u] = 0;
        }
    }

    inline void build( const int u, const int l, const int r ) {
        len[u] = r - l + 1;
        if ( l == r ) return void( mx[u] = mn[u] = sum[u] = rint() );
        int mid = l + r >> 1;
        build( u << 1, l, mid ), build( u << 1 | 1, mid + 1, r );
        pushup( u );
    }

    inline void add( const int u, const int l, const int r,
      const int al, const int ar, const int v ) {
        if ( al <= l && r <= ar ) return pushad( u, v );
        int mid = l + r >> 1; pushdn( u );
        if ( al <= mid ) add( u << 1, l, mid, al, ar, v );
        if ( mid < ar ) add( u << 1 | 1, mid + 1, r, al, ar, v );
        pushup( u );
    }

    inline void div( const int u, const int l, const int r,
      const int dl, const int dr, const int v ) {
        // if ( v == 1 ) return ;
        if ( dl <= l && r <= dr && mx[u] - floor( 1. * mx[u] / v )
          == mn[u] - floor( 1. * mn[u] / v ) ) {
            return pushad( u, floor( 1. * mx[u] / v ) - mx[u] );
        }
        int mid = l + r >> 1; pushdn( u );
        if ( dl <= mid ) div( u << 1, l, mid, dl, dr, v );
        if ( mid < dr ) div( u << 1 | 1, mid + 1, r, dl, dr, v );
        pushup( u );
    }

    inline int qmin( const int u, const int l, const int r,
      const int ql, const int qr ) {
        if ( ql <= l && r <= qr ) return mn[u];
        int mid = l + r >> 1, ret = IINF; pushdn( u );
        if ( ql <= mid ) ret = imin( ret, qmin( u << 1, l, mid, ql, qr ) );
        if ( mid < qr )
            ret = imin( ret, qmin( u << 1 | 1, mid + 1, r, ql, qr ) );
        return ret;
    }

    inline LL qsum( const int u, const int l, const int r,
      const int ql, const int qr ) {
        if ( ql <= l && r <= qr ) return sum[u];
        int mid = l + r >> 1; LL ret = 0; pushdn( u );
        if ( ql <= mid ) ret += qsum( u << 1, l, mid, ql, qr );
        if ( mid < qr ) ret += qsum( u << 1 | 1, mid + 1, r, ql, qr );
        return ret;
    }
} sgt;

signed main() {
    n = rint(), q = rint();
    sgt.build( 1, 0, n - 1 );
    for ( int op, l, r; q--; ) {
        op = rint(), l = rint(), r = rint();
        if ( op == 1 ) sgt.add( 1, 0, n - 1, l, r, rint() );
        else if ( op == 2 ) sgt.div( 1, 0, n - 1, l, r, rint() );
        else if ( op == 3 ) wint( sgt.qmin( 1, 0, n - 1, l, r ) ), puts( "" );
        else wint( sgt.qsum( 1, 0, n - 1, l, r ) ), puts( "" );
    }
    return 0;
}