洛谷P2357 守墓人 题解 数列分块

题目链接:https://www.luogu.com.cn/problem/P2357

涉及操作:

  1. 区间更新;
  2. 单点更新(直接算到区间更新里面);
  3. 区间查询;
  4. 单点查询(直接算到区间查询里面)。

解题思路:

数列分块。

\(tag[i]\) 表示第 \(i\) 个分块中每个数都增加的值;\(sum[i]\) 表示第 \(i\) 个分块中非整体增加的数值和。

示例程序:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 200020;
int n, m, blo, bl[maxn];
long long a[maxn], sum[505], tag[505];

void add(int l, int r, long long k) {
    for (int i = l; i <= min(r, bl[l]*blo); i ++)
        a[i] += k, sum[bl[i]] += k;
    if (bl[l] != bl[r]) {
        for (int i = (bl[r]-1)*blo+1; i <= r; i ++)
            a[i] += k, sum[bl[i]] += k;
    }
    for (int i = bl[l]+1; i < bl[r]; i ++)
        tag[i] += k;
}

long long query(int l, int r) {
    long long res = 0;
    for (int i = l; i <= min(r, bl[l]*blo); i ++)
        res += a[i] + tag[bl[i]];
    if (bl[l] != bl[r]) {
        for (int i = (bl[r]-1)*blo+1; i <= r; i ++)
            res += a[i] + tag[bl[i]];
    }
    for (int i = bl[l]+1; i < bl[r]; i ++)
        res += sum[i] + tag[i] * blo;
    return res;
}

int main() {
    ios::sync_with_stdio(0);
    cin >> n >> m;
    blo = sqrt(n);
    for (int i = 1; i <= n; i ++) {
        cin >> a[i];
        bl[i] = (i - 1) / blo + 1;
        sum[bl[i]] += a[i];
    }
    while (m --) {
        int op;
        cin >> op;
        if (op == 1) {
            int l, r; long long k;
            cin >> l >> r >> k;
            add(l, r, k);
        }
        else if (op == 2) {
            long long k;
            cin >> k;
            add(1, 1, k);
        }
        else if (op == 3) {
            long long k;
            cin >> k;
            add(1, 1, -k);
        }
        else if (op == 4) {
            int l, r;
            cin >> l >> r;
            cout << query(l, r) << endl;
        }
        else { // op == 5
            cout << query(1, 1) << endl;
        }
    }
    return 0;
}
posted @ 2021-11-19 13:01  quanjun  阅读(95)  评论(0编辑  收藏  举报