LOJ6283. 數列分塊入門 7 題解
阿新 • • 發佈:2021-11-08
設計操作:
- 區間加法
- 區間乘法
- 單點查詢
解題思路:
用 \(X_i\) 維護第 \(i\) 個分塊當前乘的數,用 \(Y_i\) 維護第 \(i\) 個分塊當前加的數。
若當前乘了 \(X_i\),加了 \(Y_i\),則加了 \(c\) 之後 \(\Rightarrow\) 乘的數不變,加的數變成了 \(Y_i + c\)。
若當前乘了 \(X_i\),加了 \(Y_i\),則乘了 \(c\) 之後 \(\Rightarrow\) 乘的數變成了 \(X_i \times c\),加的數變成了 \(Y_i \times c\)。
而如果加或乘的區間不完全包含,則更新所有 \(a_i\)
示例程式:
#include <bits/stdc++.h> using namespace std; const int maxn = 100010; const long long MOD = 10007; int n, blo, bl[maxn]; long long a[maxn], X[505], Y[505]; // 將第p個分塊的修改還原到分塊中的每個元素 void recover(int p) { for (int i = (p-1)*blo+1; i <= min(p*blo,n); i ++) { a[i] = (a[i] * X[p] % MOD + Y[p]) % MOD; } X[p] = 1; Y[p] = 0; } void add(int l, int r, int c) { recover(bl[l]); for (int i = l; i <= min(bl[l]*blo, r); i ++) a[i] = (a[i] + c) % MOD; if (bl[l] != bl[r]) { recover(bl[r]); for (int i = (bl[r]-1)*blo+1; i <= r; i ++) a[i] = (a[i] + c) % MOD; } for (int i = bl[l]+1; i < bl[r]; i ++) { Y[i] = (Y[i] + c) % MOD; } } void multi(int l, int r, int c) { recover(bl[l]); for (int i = l; i <= min(bl[l]*blo, r); i ++) a[i] = (a[i] * c) % MOD; if (bl[l] != bl[r]) { recover(bl[r]); for (int i = (bl[r]-1)*blo+1; i <= r; i ++) a[i] = (a[i] * c) % MOD; } for (int i = bl[l]+1; i < bl[r]; i ++) { X[i] = X[i] * c % MOD; Y[i] = Y[i] * c % MOD; } } int query(int p) { // 這樣查詢是根號n的 // recover(bl[p]); // return a[p]; // 這樣查詢是O(1)的 return (a[p] * X[bl[p]] + Y[bl[p]]) % MOD; } int main() { ios::sync_with_stdio(0); cin >> n; blo = sqrt(n); for (int i = 1; i <= n; i ++) { cin >> a[i]; bl[i] = (i - 1) / blo + 1; } for (int i = 1; i <= bl[n]; i ++) X[i] = 1; // 乘法的基數是1 for (int i = 0; i < n; i ++) { int op, l, r, c; cin >> op >> l >> r >> c; if (op == 0) { add(l, r, c); } else if (op == 1) { multi(l, r, c); } else { cout << query(r) << endl; } } return 0; }