题解:Loj 6277

题目https://loj.ac/p/6277

主要是讲解分块的做法(至于分块是什么参考别的大佬的 Blog 吧)。

这里假设你会分块。

思路

我们将区间 [1,n] 划分,每个块的长度为 \sqrt n,然后得出区间左右范围:

for (int i = 1; i <= num; i++) {
     L[i] = (i - 1) * len + 1;
     R[i] = i * len;
}

R[num] = n;

然后遍历每个区间,标记每个点所属块(sum 在本题其实没什么用):

for (int i = 1; i <= num; i++) {
        for (int j = L[i]; j <= R[i]; j++) {
            pos[j] = i;
            sum[i] += a[j];
        }
    }

对于区间修改(modify)

对于 l 和 r 属于同一个块的情况:

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值