树状数组Day1题解(3/7)+小结

1.单点修改,区间查询(简单)

单点修改,区间查询

较水,咕咕

2.区间修改,单点查询(较为简单)

区间修改,单点查询

将一个区间的数同时加上一个x,自然想到差分序列 ( p )

p[i] = a[i] - a[i - 1]

我们还知道:

a[i] = Σp[j](1 <= j <= i)

证明

p[i] = a[i] - a[i - 1]
p[i] + p[i - 1] = a[i] -  a[i - 1] + a[i - 1] - a[i - 2] = a[i] - a[i - 2]
...
p[i] + p[i - 1] + ... + p[1] = a[i] - a[i - 1] + a[i - 1] - a[i - 2] +... + a[1] - a[0] = a[i] - a[0]

又因为a[0] = 0
所以p[i] + p[i - 1] + … + p[1] + p[0] = a[i]

求单点即求差分序列的前缀和,又因为会进行修改,所以可以用树状数组维护前缀和

代码:

#include <cstdio>
#define LL long long 

const int MAXN = 1e6 + 5;
int n, m;
int a[MAXN], d[MAXN];
LL BIT[MAXN];

int _Lowbit(int x) {return x & -x;}
void _Update(int, int);
LL _Sum(int);

int main() {
	scanf("%d %d", &n, &m);
	for(int i = 1; i <= n; i++) {
		scanf("%d", &a[i]);
		d[i] = a[i] - a[i - 1];
		_Update(i, d[i]);
	}
//	for(int i = 1; i <= n; i++) printf("%lld ", _Sum(i));
	for(int i = 1; i <= m; i++) {
		int flag;
		scanf("%d", &flag);
		if(flag == 1) {
			int l, r, x;
			scanf("%d %d %d", &l, &r, &x);
			_Update(l, x);
			_Update(r + 1, -x);
		}
		else {
			int x;
			scanf("%d", &x);
			printf("%lld\n", _Sum(x));
		}
	}
	return 0;
}

void _Update(int index, int x) {
	for(int i = index; i <= n; i += _Lowbit(i))
		BIT[i] += x;
	return;
}

LL _Sum(int x) {
	LL sum = 0;
	for(int i = x; i > 0; i -= _Lowbit(i)) 
		sum += BIT[i];
	return sum;
}

3.区间修改,区间查询(难)

分析:这道题也是对一个区间进行修改,所以也同样想到差分序列,可是还要求差分序列前缀和的前缀和 (套娃),所以还要维护一个前缀和

求(1 ~ i)的前缀和(原数组),记作pre[i]

pre[i] = a[1] + a[2] + a[3] + ... + a[i - 1] + a[i]
       = (p[1]) + (p[1] + p[2]) + (p[1] + p[2] + p[3]) + ... + (p[1] + p[2] + p[3] + ... + p[i])
       = p[1] * i + p[2] * (i - 1) + p[3] * (i - 2) + ... + p[i] * 1
       = i * (p[1] + p[2] + p[3] + ... p[i]) + p[1] * (1 - 1) + p[2] * (2 - 1) + p[3] * (3 - 1) + ... + p[i] * (i - 1)

BIT1维护p[1] + p[2] + p[3] + … p[i]的前缀和
BIT2维护p[1] * (1 - 1) + p[2] * (2 - 1) + p[3] * (3 - 1) + … + p[i] * (i - 1)的前缀和
若对i进行操作
则BIT1的增量是x,BIT2的增量是x * (i - 1)

证明
欲证:该操作是对的~~/hj~~
设操作区间为[l, r]
若l <= i, r >= i
则pre[i + x] = pre[i] + x * i - x * (l - 1) = pre[i] + x * (i - l + 1)
正确
若l <= i, r <= i - 1
则pre[i + x] = pre[i] + x * r - x * (l - 1) = pre[i] + x * (r - l + 1)
正确

#include <cstdio>
#define LL long long

const int MAXN = 1e6 + 5;

int n, m;
int a[MAXN];

LL BIT1[MAXN], BIT2[MAXN];

void _Update (int, int);

LL _Sum (int);
int lowbit (int x) { return x & (-x); }

int main () {
    scanf ("%d %d", &n, &m);
    for (int i = 1; i <= n; i++) scanf ("%d", &a[i]), _Update (i, a[i] - a[i - 1]);
    for (int i = 1; i <= m; i++) {
        int type; scanf ("%d", &type);
        if (type == 1) {
            int l, r, x; scanf ("%d %d %d", &l, &r, &x);
            _Update (l, x);
            _Update (r + 1, -x);
        }
        else {
            int l, r; scanf ("%d %d", &l, &r);
            printf ("%lld\n", _Sum (r) - _Sum (l - 1));
        }
    }
    return 0;
}

void _Update (int index, int x) {
    for (int i = index; i <= n; i += lowbit (i)) {
        BIT1[i] += x;
        BIT2[i] += (long long)(index - 1) * x;
    }
}

LL _Sum (int index) {
    LL cnt = 0;
    for (int i = index; i > 0; i -= lowbit (i)) {
        cnt += BIT1[i] * index;
        cnt -= BIT2[i];
    }
    return cnt;
}
posted @ 2020-07-26 21:01  C2022lihan  阅读(33)  评论(0编辑  收藏  举报