P3368【模板】树状数组2

树状数组
输入一个数组,那么我们所建立的数组:

1 (10) = 1(2)
第一位(1在二进制下=1 二进制下的第一个1,1=1)的值为输入的数组的第一位往前的一位的和,也就是第一位。
2(10) = 10(2) 
第二位(2在二进制下=10 二进制下的第一个1,10=2)的值为输入的数组的第二位往前两位的和,第一位和第二位。
3(10) = 11(2)
第三位(3在二进制下=11 二进制下的第一个1,1=1)的值为输入的数组的第三位往前一位的和,也就是第三位。
4(10) = 100(2)
第四位的值(4在二进制下=100 二进制下的第一个1,100=4)的值为输入的数组的第四位往前四位的和,也就是第一位,第二位,第三位以及第四位。

本题也是利用树状数组,不过在插入树状数组的时候插入值是一个差分值。
这种思想很有趣。
因为本题是修改连续区间的值,但是直接插入数组如:
arr : 1 2 3 4 5
val : 1 4 8 5 2
差分值 : 1 3 4 -3 -3
从1到4都加2的话,维护太慢了也太麻烦,如果是维护他们的当前项和前一项的差值 ,那么如:
arr : 1 2 3 4 5
val : 3 6 10 7 2
差分值 : 3 3 4 -3 -5
只是改变第一项和第4+1项
我们会发现:差分值相加得到的值就是当前值

#include 
using namespace std;

#define LBS(_a) ((_a) & -(_a))
#define SIZE 5000005

int n, m;
int arr[SIZE];

int sum(int i) {
    int sum = 0;
    while (i > 0) {
        sum += arr[i], i -= LBS(i);
    }
    return sum;
}

void add(int i, int value) {
    while (i <= n) {
        arr[i] += value, i += LBS(i);
    }
    return ;
}

int main() {
    cin >> n >> m;
    int val, ans = 0;
    for (int i = 1; i <= n; i++) {
        cin >> val;
        ans = val - ans;
        add(i, ans);
        ans = val;
    }
    int x, y, z, s;
    while (m > 0) {
        cin >> x ; 
        if (x == 1) {
            cin >> y >> z >> s;
            add(y, s);
            add(z + 1, -s); // [y, z] 只改变arr[y], arr[z+1]
        }
        if (x == 2) {
            cin >> s;
            cout << sum(s) << endl;
        }
        m -= 1;    
    } 
    return 0;
}

你可能感兴趣的:(P3368【模板】树状数组2)