主要是讲解分块的做法(至于分块是什么参考别的大佬的 Blog 吧)。
这里假设你会分块。
思路
我们将区间 划分,每个块的长度为
,然后得出区间左右范围:
for (int i = 1; i <= num; i++) {
L[i] = (i - 1) * len + 1;
R[i] = i * len;
}
R[num] = n;
然后遍历每个区间,标记每个点所属块( 在本题其实没什么用):
for (int i = 1; i <= num; i++) {
for (int j = L[i]; j <= R[i]; j++) {
pos[j] = i;
sum[i] += a[j];
}
}
对于区间修改(modify)
对于 和
属于同一个块的情况:
for (int i = l; i <= r; i++) {
a[i] += val, sum[p] += val;
}
否则,先处理掉周边不能构成整个块的情况:
for (int i = l; i <= R[p]; i++) {
a[i] += val, sum[p] += val;
}
for (int i = L[q]; i <= r; i++) {
a[i] += val, sum[q] += val;
}
这里沿用一下线段树中的懒标记这个概念,对于整个块,如果就这么直接暴力遍历过去,和暴力枚举没什么区别(至少都过不了)。那么,既然是整个块的区间都要加,我们直接用数组存储这个块需要加多少,在查询之后再进行计算,而不是一开始就累加。
直接就给完整代码吧,反正也到尾声了。
实现
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n, a[50005], L[50005], R[50005], pos[50005], sum[50005], lazy[50005];
void init() {
int len = sqrt(n);
int num = (n + len - 1) / len;
for (int i = 1; i <= num; i++) {
L[i] = (i - 1) * len + 1;
R[i] = i * len;
}
R[num] = n;
for (int i = 1; i <= num; i++) {
for (int j = L[i]; j <= R[i]; j++) {
pos[j] = i;
sum[i] += a[j];
}
}
}
void modify(int l, int r, int val) {
int p = pos[l], q = pos[r];
if (p == q) {
for (int i = l; i <= r; i++) {
a[i] += val, sum[p] += val;
}
} else {
for (int i = l; i <= R[p]; i++) {
a[i] += val, sum[p] += val;
}
for (int i = L[q]; i <= r; i++) {
a[i] += val, sum[q] += val;
}
for (int i = p + 1; i <= q - 1; i++) {
lazy[i] += val;
}
}
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
init();
int id, l, r, c;
while (n--) {
cin >> id >> l >> r >> c;
if (id) {
cout << a[r] + lazy[pos[r]] << '\n';
} else {
modify(l, r, c);
}
}
return 0;
}
296

被折叠的 条评论
为什么被折叠?



