Solution -「UOJ #46」玄學
阿新 • • 發佈:2021-07-08
\(\mathcal{Description}\)
Link.
給定序列 \(\{a_n\}\) 和 \(q\) 次操作,操作內容如下:
- 給出 \(l,r,k,b\),宣告一個修改方案,表示 \(\forall i\in[l,r],~a_i\leftarrow (ka_i+b)\bmod m\)。
- 給出 \(l,r,x\),求將第 \(l\) 到第 \(r\) 個修改方案作用於序列時,\(a_x\) 的值。
強制線上,\(n\le10^5\),\(q\le6\times10^5\)。
\(\mathcal{Solution}\)
一種類似線上建線段樹的 trick。
若允許離線,自然可以建立關於修改方案的線段樹,每個結點維護對應區間內的修改依次作用後,每個 \(a\)
轉為線上,注意到當線段樹結點對應區間內的修改操作全部宣告時,這個結點的資訊才有效,所以當且僅當區間內修改操作全部宣告時,在結點處歸併左右兒子資訊,均攤複雜度就是離線建樹的複雜度。最終複雜度為 \(\mathcal O(n\log n+q\log^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( '\n' ); } } return 0; }