分块
分块基本思想:
- 将长度为 \(n\) 的序列划分成 \(\sqrt n\) 块,每块的元素个数为 \(\sqrt n\)。
- 维护 \(pos[i]\) 表示下标 \(i\) 的元素所在块的编号,维护 \(L[i]\) 和 \(R[i]\) 表示第 \(i\) 块的起始下标和中止下标。
- 每个块维护相应的信息,比如区间和等。
- 维护块的标记 \(tag[i]\),表示第 \(i\) 个块的修改信息
- 对于一次询问 \([x, y]\):
5. 1. 若 \(x\) 和 \(y\) 在同一个块中,则暴力枚举找答案,时间复杂度 \(O(\sqrt n)\)。
5. 2. 若 \(x\) 和 \(y\) 不在同一个块,分为 \(3\) 部分处理:
3. 2. 1. \(x\) 所在块,暴力枚举, \(O(\sqrt n)\)。
4. 2. 2. \(y\) 所在块,暴力枚举,\(O(\sqrt n)\)。
5. 2. 3. \(x\) 和 \(y\) 中间完整的块,整块枚举, \(O(\sqrt n)\)。
6. 3. 询问时不论是完整的块还是不完整的块,都要读取 \(tag[i]\)。 - 对于一次修改 \([x, y]\),增加 \(val\)。方式与询问类似。
6. 1. 枚举完整的块时,修改 \(tag[i]\),否则暴力修改原数组。
LOJ 6277. 数列分块入门 1
纯模板
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
using i64 = long long;
const int N = 50010, V = 310;
int n, s;
int a[N];
int L[V], R[V];
i64 sum[V], tag[V];
int p[N];
void modify(int l, int r, i64 v) {
int p1 = p[l], p2 = p[r];
if (p1 == p2) {
for (int i = l; i <= r; i++)
a[i] += v;
sum[p1] += v * (r - l + 1);
} else {
for (int i = l; i <= R[p1]; i++)
a[i] += v;
sum[p1] += v * (R[p1] - l + 1);
for (int i = L[p2]; i <= r; i++)
a[i] += v;
sum[p2] += v * (r - L[p2] + 1);
for (int i = p1 + 1; i <= p2 - 1; i++) {
sum[i] += (R[i] - L[i] + 1) * v;
tag[i] += v;
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
s = sqrt(n);
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= s; i++) {
L[i] = R[i - 1] + 1;
R[i] = L[i] + s - 1;
}
if (R[s] != n) {
s++;
L[s] = L[s - 1] + 1;
R[s] = n;
}
for (int j = 1; j <= s; j++) {
for (int i = L[j]; i <= R[j]; i++) {
p[i] = j;
sum[j] += a[i];
}
}
int T = n;
int opt, x, y, z;
while (T--) {
cin >> opt >> x >> y >> z;
if (opt) {
cout << a[y] + tag[p[y]] << '\n';
} else {
modify(x, y, z);
}
}
return 0;
}
LOJ 6278. 数列分块入门 2
开两个数组 \(a, b\),一个用来统计整块的信息,一个用来统计零碎的部分。
每次更新信息时,两个数组一起更新,不同的是,\(a\) 数组要排序,\(b\) 数组不要排序。
为什么要 \(b\) 数组呢?因为如果排过序后 \([l, r]\) 区间就不准确了,变成排过序后的区间了。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
using i64 = long long;
const int N = 500010, V = 300;
struct Node {
i64 x;
int id;
}a[N], b[N];
bool cmp(const Node a, const Node b) {
if (a.x != b.x) return a.x < b.x;
return a.id < b.id;
}
int n, s;
i64 tag[V];
int L[V];
int R[V];
int p[N];
void update_info(int l, int r) {
sort(a + l, a + r + 1, cmp);
for (int j = l; j <= r; j++)
b[a[j].id].id = j;
}
void modify(int l, int r, i64 x) {
int p1 = p[l], p2 = p[r];
if (p1 == p2) {
for (int i = l; i <= r; i++) {
b[i].x += x;
a[b[i].id].x += x;
}
update_info(L[p1], R[p1]);
}
else {
for (int i = l; i <= R[p1]; i++) {
b[i].x += x;
a[b[i].id].x += x;
}
update_info(L[p1], R[p1]);
for (int i = L[p2]; i <= r; i++) {
b[i].x += x;
a[b[i].id].x += x;
}
update_info(L[p2], R[p2]);
for (int i = p1 + 1; i <= p2 - 1; i++) tag[i] += x;
}
}
int find_value(int l, int r, i64 x, int _tag) {
int st = l;
l--, r++;
while (l + 1 < r) {
int mid = (l + r) / 2;
if (a[mid].x + _tag < x) l = mid;
else r = mid;
}
return l - st + 1;
}
int query(int l, int r, i64 z) {
int p1 = p[l], p2 = p[r];
if (p1 == p2) {
int res = 0;
for (int i = l; i <= r; i++)
if (b[i].x + tag[p1] < z)
res++;
return res;
}
else {
int res = 0;
for (int i = l; i <= R[p1]; i++)
if (b[i].x + tag[p1] < z)
res++;
for (int i = L[p2]; i <= r; i++)
if (b[i].x + tag[p2] < z)
res++;
for (int i = p1 + 1; i <= p2 - 1; i++)
res += find_value(L[i], R[i], z, tag[i]);
return res;
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i].x;
a[i].id = i;
b[i].x = a[i].x;
}
s = sqrt(n);
for (int i = 1; i <= s; i++) {
L[i] = R[i - 1] + 1;
R[i] = L[i] + s - 1;
}
if (R[s] != n) {
s++;
L[s] = R[s - 1] + 1;
R[s] = n;
}
for (int i = 1; i <= s; i++) {
update_info(L[i], R[i]);
for (int j = L[i]; j <= R[i]; j++) p[j] = i;
}
int opt, x, y;
i64 z;
for (int i = 1; i <= n; i++) {
cin >> opt >> x >> y >> z;
if (opt == 0) modify(x, y, z);
else cout << query(x, y, z * z) << '\n';
}
return 0;
}
LOJ 6279. 数列分块入门 3
与上一题类似,只是把求个数变成求最大值。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
using i64 = long long;
const int N = 100010, V = 410;
int n, s;
int L[V], R[V];
i64 tag[V];
int p[N];
struct Node {
i64 x;
int id;
} a[N], b[N];
bool cmp(const Node a, const Node b) {
if (a.x != b.x)
return a.x < b.x;
return a.id < b.id;
}
void update_info(int l, int r) {
sort(a + l, a + r + 1, cmp);
for (int i = l; i <= r; i++) {
b[a[i].id].id = i;
}
}
void modify(int l, int r, i64 x) {
int p1 = p[l], p2 = p[r];
if (p1 == p2) {
for (int i = l; i <= r; i++) {
b[i].x += x;
a[b[i].id].x += x;
}
update_info(L[p1], R[p1]);
} else {
for (int i = l; i <= R[p1]; i++) {
b[i].x += x;
a[b[i].id].x += x;
}
update_info(L[p1], R[p1]);
for (int i = L[p2]; i <= r; i++) {
b[i].x += x;
a[b[i].id].x += x;
}
update_info(L[p2], R[p2]);
for (int i = p1 + 1; i <= p2 - 1; i++)
tag[i] += x;
}
}
int find_value(int l, int r, i64 x, i64 _tag) {
l--;
r++;
while (l + 1 < r) {
int mid = (l + r) / 2;
if (a[mid].x + _tag < x)
l = mid;
else
r = mid;
}
return l;
}
int query(int l, int r, i64 x) {
int p1 = p[l], p2 = p[r];
if (p1 == p2) {
i64 res = -1;
for (int i = l; i <= r; i++) {
if (b[i].x + tag[p1] < x) {
res = max(res, b[i].x + tag[p1]);
}
}
return res;
} else {
i64 res = -1;
for (int i = l; i <= R[p1]; i++) {
if (b[i].x + tag[p1] < x) {
res = max(res, b[i].x + tag[p1]);
}
}
for (int i = L[p2]; i <= r; i++) {
if (b[i].x + tag[p2] < x) {
res = max(res, b[i].x + tag[p2]);
}
}
for (int i = p1 + 1; i <= p2 - 1; i++) {
int f = find_value(L[i], R[i], x, tag[i]);
if (f < L[i])
continue;
res = max(res, a[f].x + tag[i]);
}
return res;
}
}
int main() {
#ifndef DEBUG
ios::sync_with_stdio(false);
cin.tie(nullptr);
#endif
cin >> n;
s = sqrt(n);
for (int i = 1; i <= n; i++) {
cin >> a[i].x;
b[i].x = a[i].x;
a[i].id = i;
}
for (int i = 1; i <= s; i++) {
L[i] = R[i - 1] + 1;
R[i] = L[i] + s - 1;
}
if (R[s] != n) {
s++;
L[s] = R[s - 1] + 1;
R[s] = n;
}
for (int j = 1; j <= s; j++) {
for (int i = L[j]; i <= R[j]; i++)
p[i] = j;
update_info(L[j], R[j]);
}
int opt, x, y, z;
for (int i = 1; i <= n; i++) {
cin >> opt >> x >> y >> z;
if (opt == 0)
modify(x, y, z);
else
cout << query(x, y, z) << '\n';
}
return 0;
}
LOJ 6280. 数列分块入门 4
纯模板
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
using i64 = long long;
const int N = 500010, V = 510;
int n, s;
int a[N], p[N];
int L[V], R[V], tag[V];
i64 sum[V];
int query(int l, int r, const int mod) {
int p1 = p[l], p2 = p[r];
if (p1 == p2) {
i64 res = 0;
for (int i = l; i <= r; i++) res = (res + a[i] + tag[p1]) % mod;
return res;
}
else {
i64 res = 0;
for (int i = l; i <= R[p1]; i++) res = (res + a[i] + tag[p1]) % mod;
for (int i = L[p2]; i<= r; i++) res = (res + a[i] + tag[p2]) % mod;
for (int i = p1 + 1; i <= p2 - 1; i++) res = (res + sum[i]) % mod;
return res;
}
}
void modify(int l, int r, i64 x) {
int p1 = p[l], p2 = p[r];
if (p1 == p2) {
for (int i = l; i <= r; i++) a[i] += x;
sum[p1] += x * (r - l + 1);
}
else {
for (int i = l; i <= R[p1]; i++) a[i] += x;
sum[p1] += x * (R[p1] - l + 1);
for (int i = L[p2]; i <= r; i++) a[i] += x;
sum[p2] += x * (r - L[p2] + 1);
for (int i = p1 + 1; i <= p2 - 1; i++) {
tag[i] += x;
sum[i] += x * (R[i] - L[i] + 1);
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
s = sqrt(n);
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= s; i++) {
L[i] = R[i - 1] + 1;
R[i] = L[i] + s - 1;
}
if (R[s] != n) {
s++;
L[s] = R[s - 1] + 1;
R[s] = n;
}
for (int j = 1; j <= s; j++) {
for (int i = L[j]; i <= R[j]; i++) {
sum[j] += a[i];
p[i] = j;
}
}
int opt, x, y, z;
for (int i = 1; i <= n; i++) {
cin >> opt >> x >> y >> z;
if (opt == 1) cout << query(x, y, z + 1) << '\n';
else modify(x, y, z);
}
return 0;
}