【树状数组】
顾名思义:本质是数组,逻辑上是被构造出来的二叉树,方便求前缀和,以及更新元素值
关键:设原数组为A,设构造出来的树状数组为 C,令
C i = A i-lowbit(i) + 1 + A i-lowbit(i) + 2 + .......... + Ai , 其中lowbit(i) = i & (-i)
所以
操作1:求前缀和 Si = Ci + Ci-lowbit(i) + ...... 直到 C 的下标 index = i - lowbit(i) = 1
操作2:元素A[x] 的值加上 y ,需要更新包含A[x] 的区间:
C[x] = C[x] + y
x = x + lowbit(x) C[x] = C[ x ] + y
........
直到 x > n (n 为元素个数)
可以得以上两个操作的时间复杂度均为 O( logn )
【应用】
求逆序对
【优化】
【模板】
/*树状数组模板注意数组C[]下标是从1开始by chsobin*/const int maxn = 32005; int c[maxn]; void init(){ memset(c, 0, sizeof(c)); } int lowbit(int x){ return x & (-x); } void update(int x, int add){ while(x<=maxn){ c[x] += add; x += lowbit(x); } } int getsum(int x){ int s = 0; while(x > 0){ s += c[x]; x -= lowbit(x); } return s; }
【题目】
hdu1541
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int maxn = 32005; int c[maxn]; int ans[maxn]; void init(){ memset(c, 0, sizeof(c)); memset(ans, 0, sizeof(ans)); } int lowbit(int x){ return x & (-x); } void update(int x, int add){ while(x<=maxn){ c[x] += add; x += lowbit(x); } } int getsum(int x){ int s = 0; while(x > 0){ s += c[x]; x -= lowbit(x); } return s; } int main(){ int n, x, y; while(~scanf("%d", &n)){ init(); for(int i=0;i<n;++i){ scanf("%d%d", &x, &y); x++; //很重要,被这个坑了,一直超时 ans[getsum(x)]++; update(x, 1); } for(int i=0;i<n;++i) printf("%d\n", ans[i]); } return 0; }
hdu4970
树状数组区间更新
参考自http://blog.csdn.net/idealism_xxm/article/details/51051342
【参考】
来源:https://www.cnblogs.com/chsobin/p/8323884.html