LOJ6283. 数列分块入门 7 题解
设计操作:
- 区间加法
- 区间乘法
- 单点查询
解题思路:
用 \(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\),同时还原对应的分段 \(p\) 的 \(X_p = 1, Y_p = 0\)(因为乘法的基数为 \(1\))。
示例程序:
#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;
}