树状数组模板2

112 阅读2分钟

P3368 【模板】树状数组 2 - 洛谷

题目描述

如题,已知一个数列,你需要进行下面两种操作:

  1. 将某区间每一个数加上 xx

  2. 求出某一个数的值。

输入格式

第一行包含两个整数 NNMM,分别表示该数列数字的个数和操作的总个数。

第二行包含 NN 个用空格分隔的整数,其中第 ii 个数字表示数列第 ii 项的初始值。

接下来 MM 行每行包含 2244个整数,表示一个操作,具体如下:

操作 11: 格式:1 x y k 含义:将区间 [x,y][x,y] 内每个数加上 kk

操作 22: 格式:2 x 含义:输出第 xx 个数的值。

输出格式

输出包含若干行整数,即为所有操作 22 的结果。

样例输入 #1

5 5
1 5 4 2 3
1 2 4 2
2 3
1 1 5 -1
1 3 5 7
2 4

样例输出 #1

6
10

数据约束

1N,M500000,  1x,yn1≤N,M≤500000,\;1≤x,y≤n,保证任意时刻序列中任意元素的绝对值都不大于 2302^{30}

题目分析

相较于上一题而言,本题所作出的改变为区间修改。

我们知道上次每次单点修改数值的复杂度为 O(logn)O(logn),若本题仍用这种解法,则单纯修改数值的总复杂度为 O(n2logn)O(n^2logn), 实在是卧龙凤雏。

在这里,我们引入差分数组的解法。我们知道对于差分数组而言,当范围修改原数组某一段区间的数值时,相当于修改差分数组的两个范围的边界值。

而对于本题的查询操作,对于单点数值的查询,相当于求差分数组的前缀和,与树状数组的适用不谋而合。

这样,我们只需要将原本的数组转换为差分数组,并按树状数组的方式维护,最终修改数值的复杂的为 O(logn)O(logn),查询数值的复杂度也为 O(logn)O(logn)

Accept代码

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

const int N = 500010;

int n, m;
int a[N], c[N];

void modify(int x, int k)
{
    for ( ; x <= n; x += x & (-x)) c[x] += k;
}

int query(int x)
{
    int s = 0;
    for ( ; x; x -= x & (-x)) s += c[x];
    return s;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> n >> m;
    for (int i = 1; i <= n; i ++)
    {
        cin >> a[i];
        modify(i, a[i] - a[i - 1]);
    }

    for (int i = 0; i < m; i ++)
    {
        int op; cin >> op;
        if (op == 1)
        {
            int x, y, k;
            cin >> x >> y >> k;
            modify(x, k), modify(y + 1, -k);
        }
        else
        {
            int x; cin >> x;
            cout << query(x) << "\n";
        }
    }
    return 0;
}