zoukankan      html  css  js  c++  java
  • Solution -「UOJ #46」玄学

    (mathcal{Description})

      Link.

      给定序列 ({a_n})(q) 次操作,操作内容如下:

    1. 给出 (l,r,k,b),声明一个修改方案,表示 (forall iin[l,r],~a_ileftarrow (ka_i+b)mod m)
    2. 给出 (l,r,x),求将第 (l) 到第 (r) 个修改方案作用于序列时,(a_x) 的值。

      强制在线,(nle10^5)(qle6 imes10^5)

    (mathcal{Solution})

      一种类似在线建线段树的 trick。

      若允许离线,自然可以建立关于修改方案的线段树,每个结点维护对应区间内的修改依次作用后,每个 (a) 值会变成的 (ka+b)。不同的 ((k,b)) 形成的区间个数是与结点对应区间长度同阶的,所以一共有 (mathcal O(nlog n)) 个区间。查询时,在每个区间内二分找到会影响 (x) 位置的 ((k,b)),更新答案,单次复杂度是 (mathcal O(log^2n)) 的。

      转为在线,注意到当线段树结点对应区间内的修改操作全部声明时,这个结点的信息才有效,所以当且仅当区间内修改操作全部声明时,在结点处归并左右儿子信息,均摊复杂度就是离线建树的复杂度。最终复杂度为 (mathcal O(nlog n+qlog^2n))

    (mathcal{Code})

    /*~Rainybunny~*/
    
    #include <cstdio>
    #include <vector>
    #include <cassert>
    
    #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 )
    
    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' );
    }
    
    const int MAXN = 1e5, MAXQ = 6e5;
    int type, n, M, q, ary[MAXN + 5];
    
    inline int mul( const long long a, const int b ) { return a * b % M; }
    inline int add( const long long a, const int b ) { return ( a + b ) % M; }
    
    struct Section {
        int l, r, k, b;
        inline bool operator < ( const Section& s ) const {
            return l != s.l ? l < s.l : r < s.r;
        }
    };
    typedef std::vector<Section> Atom;
    
    inline Atom mergeSec( const Atom& u, const Atom& v ) {
        static Atom ret; ret.clear();
        int i = 0, j = 0, las = 1, us = int( u.size() ), vs = int( v.size() );
        while ( i < us && j < vs ) {
            if ( u[i].r >= v[j].r ) {
                ret.push_back( { las, v[j].r, mul( u[i].k, v[j].k ),
                  add( mul( v[j].k, u[i].b ), v[j].b ) } );
                las = v[j].r + 1;
                if ( u[i].r == v[j++].r ) ++i;
            } else {
                ret.push_back( { las, u[i].r, mul( u[i].k, v[j].k ),
                  add( mul( v[j].k, u[i].b ), v[j].b ) } );
                las = u[i++].r + 1;
            }
        }
        assert( las == n + 1 );
        return ret;
    }
    
    struct SegmentTree {
        Atom sec[MAXQ << 2];
        int upc[MAXQ << 2];
    
        inline void insert( const int u, const int l, const int r,
          const int x, const int i, const int j,  const int a, const int b ) {
            if ( l == r ) {
                ++upc[u];
                if ( i > 1 ) sec[u].push_back( { 1, i - 1, 1, 0 } );
                sec[u].push_back( { i, j, a, b } );
                if ( j < n ) sec[u].push_back( { j + 1, n, 1, 0 } );
                return ;
            }
            int mid = l + r >> 1;
            if ( x <= mid ) insert( u << 1, l, mid, x, i, j, a, b );
            else insert( u << 1 | 1, mid + 1, r, x, i, j, a, b );
            if ( ( upc[u] = upc[u << 1] + upc[u << 1 | 1] ) == r - l + 1 ) {
                sec[u] = mergeSec( sec[u << 1], sec[u << 1 | 1] );
            }
        }
    
        inline void query( const int u, const int l, const int r,
          const int ql, const int qr, const int x, int& v ) {
            if ( ql <= l && r <= qr ) {
                int sid = std::upper_bound( sec[u].begin(), sec[u].end(),
                  Section{ x + 1, 0, 0, 0 } ) - sec[u].begin() - 1;
                assert( 0 <= sid && sid < int( sec[u].size() ) );
                assert( sec[u][sid].l <= x && x <= sec[u][sid].r );
                v = add( mul( v, sec[u][sid].k ), sec[u][sid].b );
                return ;
            }
            int mid = l + r >> 1;
            if ( ql <= mid ) query( u << 1, l, mid, ql, qr, x, v );
            if ( mid < qr ) query( u << 1 | 1, mid + 1, r, ql, qr, x, v );
        }
    } sgt;
    
    int main() {
        type = rint() & 1, n = rint(), M = rint();
        rep ( i, 1, n ) ary[i] = rint();
        q = rint();
        for ( int qid = 1, ans = 0, cnt = 0, op, i, j, a, b; qid <= q; ++qid ) {
            op = rint(), i = rint(), j = rint(), a = rint();
            if ( type ) i ^= ans, j ^= ans;
            if ( op == 1 ) {
                b = rint();
                sgt.insert( 1, 1, q, ++cnt, i, j, a, b );
            } else {
                if ( type ) a ^= ans;
                sgt.query( 1, 1, q, i, j, a, ans = ary[a] );
                wint( ans ), putchar( '
    ' );
            }
        }
        return 0;
    }
    
    
  • 相关阅读:
    python两个dict相加
    rpm命令
    python logging模块不支持多进程写到一个log文件
    技术论坛地址收集
    visudo命令编辑修改/etc/sudoers配置文件
    健康是什么
    .net文件类型种种
    禁止脚本的运行
    静态页面的值传递
    datagrid数据导出到excel文件给客户端下载的几种方法 (转)
  • 原文地址:https://www.cnblogs.com/rainybunny/p/14984831.html
Copyright © 2011-2022 走看看