树状数组
树状数组实际上是个数组,维护一个区间,支持查询区间的前缀和,以及修改单点的值。
定义lowbit(x)=x&(-x),表示将x的二进制数只保留从右往左数的第一个1后的数字。
设树状数组为C,规定C[i]维护区间[i-lowbit(i)+1,i]的数字和。因此,要访问C[i]维护的区间的前一个区间,则访问C[i-lowbit(i)];要访问包含C[i]维护的区间的区间,则访问C[i+lowbit(i)](规律)。
因此,区间查询某前缀[1,i],则不断查询C[i]再查询C[i]前一个区间C[i-lowbit(i)]。要单点修改,则不断更改C[i]和包含它的C[i+lowbit(i)]。
luogu3374 【模板】树状数组1
题目大意:已知一个数列,你需要进行下面两种操作:1.将某一个数加上x;2.求出某区间每一个数的和。
1操作就是规定动作;2操作输出r前缀和减去l-1前缀和。
#include <cstdio> #include <cassert> using namespace std; const int MAX_N = 500010; int C[MAX_N], A[MAX_N]; int N; int Lowbit(int x) { return x&(-x); } int Sum(int p) { int ans = 0; while (p) { ans += C[p]; p -= Lowbit(p); } return ans; } void Modify(int p, int delta) { while (p <= N) { C[p] += delta; p += Lowbit(p); } } void Init() { for (int i = 1; i <= N; i++) Modify(i, A[i]); } int main() { #ifdef _DEBUG freopen("c:\\noi\\source\\input.txt", "r", stdin); #endif int m, op, p, delta, l, r; scanf("%d%d", &N, &m); for (int i = 1; i <= N; i++) scanf("%d", i + A); Init(); while (m--) { scanf("%d", &op); switch (op) { case 1: scanf("%d%d", &p, &delta); Modify(p, delta); break; case 2: scanf("%d%d", &l, &r); printf("%d\n", Sum(r) - Sum(l - 1)); break; default: assert(0); } } return 0; }
luogu3368 【模板】树状数组2
题目大意:已知一个数列,你需要进行下面两种操作:1.将某区间每一个数数加上x;2.求出某一个数的和。
先不考虑树状数组的问题。设原序列为A,令B[i]=A[i]-A[i-1]。查询点,相当于A[i]=sum (foreach j=1 to i)B[i]。更改一个区间[i,j],就相当于B[i]+1,B[j+1]-1。用树状数组C维护B,就可满足树状数组“单点修改,区间查询”的操作了。
#include <cstdio> #include <cstring> #include <cassert> using namespace std; const int MAX_N = 500010; int A[MAX_N], B[MAX_N], C[MAX_N]; int N; int Lowbit(int x) { return x&(-x); } int Sum(int p) { int sum = 0; while (p) { sum += C[p]; p -= Lowbit(p); } return sum; } void Update(int p, int delta) { while (p <= N) { C[p] += delta; p += Lowbit(p); } } int main() { #ifdef _DEBUG freopen("c:\\noi\\source\\input.txt", "r", stdin); #endif int opCnt, op, l, r, k, x; scanf("%d%d", &N, &opCnt); for (int i = 1; i <= N; i++) { scanf("%d", i + A); B[i] = A[i] - A[i - 1]; Update(i, B[i]); } while (opCnt--) { scanf("%d", &op); switch (op) { case 1: scanf("%d%d%d", &l, &r, &k); Update(l, k); Update(r + 1, -k); break; case 2: scanf("%d", &x); printf("%d\n", Sum(x)); break; } } return 0; }