树状数组
2016-04-13 11:02
399 查看
树状数组
对于序列a,我们设一个数组C◦
C[i] = a[i – 2^k + 1] + … + a[i]
◦ k为i在二进制下末尾0的个数
◦ i从1开始算!
其中2^k=i&(-i) #define Lowbit(x) (x & (-x)) 则C[i] = a[i – Lowbit(i) + 1] + … + a[i] 则有: C1=A1 C2=A1+A2 C3=A3 C4=A1+A2+A3+A4 C5=A5 C6=A5+A6 C7=A7 C8=A1+A2+A3+A4+A5+A6+A7+A8 ………… C16=A1+A2+A3+A4+A5+A6+A7+A8+A9+A10+A11+A12+A13+A14+A15+A16
C即为a的树状数组
结构图:
求和:
设sum(k) = a[1]+a[2]+…+a[k],则 a[i] + a[i+1] + … + a[j] = sum(j)-sum(i-1)
有了树状数组,sum(k)就能在O(logN)时间内求出,N是a数组元素个数。而且更新一个a的元素所花的
时间也是O(logN)的(a更新了C也得更新)。
sum(k) = C[n1]+C[n2] + …+ C[nm] 其中 nm= k
ni-1 = ni- lowbit(ni) 而且 n1 – lowbit(n1 ) 必须小于或等于0
求sum(r)代码:
int sum(int r) { int sumr = 0,nr = r; while(nr > 0) { sumr = sumr + c[nr]; nr = nr - Lowbit(nr); } return sumr; }
更新:
如果a[i]更新了,那么以下的几项都需要更新:
C[n1], C[n2], …C[nm]
其中,n1 = i ,ni+1 = ni + lowbit(ni)
nm + lowbit(nm) 必须大于 a 的元素个数 N
更新代码:
void Add(int x,int change,int n) { while(x <= n) { c[x] = c[x] + change; x = x + Lowbit(x); } }
树状数组的两类操作
单点更新,区间求和(这个很好理解)
//lightoj 1112 #include <set> #include <map> #include <list> #include <cmath> #include <ctime> #include <deque> #include <queue> #include <stack> #include <cctype> #include <cstdio> #include <string> #include <vector> #include <cassert> #include <cstdlib> #include <cstring> #include <sstream> #include <iostream> #include <algorithm> #define pi acos(-1.0) #define maxn (100000 + 50) #define Lowbit(x) (x & (-x)) using namespace std; typedef long long int LLI; int a[maxn]; int c[maxn]; void Update(int x,int change,int n) { while(x <= n) { c[x] = c[x] + change; x = x + Lowbit(x); } } int Query(int l,int r) { int suml = 0,sumr = 0,nl = l - 1,nr = r; while(nr > 0) { sumr = sumr + c[nr]; nr = nr - Lowbit(nr); } while(nl > 0) { suml = suml + c[nl]; nl = nl - Lowbit(nl); } return sumr - suml; } int main() { // freopen("in.txt", "r", stdin); // freopen("out.txt", "w", stdout); int t; scanf("%d",&t); for(int Case = 1; Case <= t; Case ++) { memset(a,0,sizeof(a)); memset(c,0,sizeof(c)); printf("Case %d:\n",Case); int n,q,sum = 0,temp; scanf("%d%d",&n,&q); for(int i = 1; i <= n; i ++) { scanf("%d",&temp); sum = sum + temp; a[i] = sum; c[i] = a[i] - a[i - Lowbit(i)]; } for(int i = n; i >= 1; i --) { a[i] = a[i] - a[i - 1]; } for(int i = 1; i <= q; i ++) { int Order,ii,jj; scanf("%d",&Order); if(Order == 1) { scanf("%d",&ii); Update(ii + 1,-a[ii + 1],n); printf("%d\n",a[ii + 1]); a[ii + 1] = 0; } else if(Order == 2) { scanf("%d%d",&ii,&jj); a[ii + 1] = a[ii + 1] + jj; Update(ii + 1,jj,n); } else { scanf("%d%d",&ii,&jj); printf("%d\n",Query(ii + 1,jj + 1)); } } } return 0; }
区间重点内容更新,单点求值 更改区间[x , y],区间[x , y]里面的每个数全部加上val , 查询点k的值 区间[x , y]加上val相当于点x加上val , 点y+1减去val,那么求k点的值就等于[1,k]的和 其实更新[x,y]区间内的值在更新的时候并没有直接更新 而是把a[x]的值加上了val,代表由此以后的所有都加val,相应的a[y+1]应该加上-val, 这样在求k点的值的时候,直接求前缀和,如果x<=k<=y,那么求和时候会有val的加成相当于a[k]+val, 如果k<x那么区间[x,y]的修改与k无关,如果k>y那么a[k]+val然后还会-val相当于没加没减
如:lightoj 1080
#include <set>
#include <map>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <queue>
#include <stack>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <cassert>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#define pi acos(-1.0)
#define maxn (100000 + 50)
#define Lowbit(x) (x & (-x))
using namespace std;
typedef long long int LLI;
char a[maxn];
int c[maxn];
void Add(int x,int change,int n) { while(x <= n) { c[x] = c[x] + change; x = x + Lowbit(x); } }
int Query(int r) {
int sumr = 0,nr = r;
while(nr > 0) {
sumr = sumr + c[nr];
nr = nr - Lowbit(nr);
}
return sumr;
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
int t,n;
scanf("%d",&t);
for(int Case = 1; Case <= t; Case ++) {
printf("Case %d:\n",Case);
memset(c,0,sizeof(c));
scanf("%s",a + 1);
int len = strlen(a + 1);
scanf("%d",&n);
for(int i = 1; i <= n; i ++) {
char Order[2];
scanf("%s",Order);
if(Order[0] == 'I') {
int l, r;
scanf("%d%d",&l,&r);
Add(l,1,len);
Add(r + 1,-1,len);
} else if(Order[0] == 'Q') {
int id;
scanf("%d",&id);
if(Query(id) & 1) {
if(a[id] == '0') printf("1\n");
else printf("0\n");
} else printf("%c\n",a[id]);
}
}
}
return 0;
}
相关文章推荐
- C#数据结构之顺序表(SeqList)实例详解
- Lua教程(七):数据结构详解
- 解析从源码分析常见的基于Array的数据结构动态扩容机制的详解
- C#数据结构之队列(Quene)实例详解
- C#数据结构揭秘一
- C#数据结构之单链表(LinkList)实例详解
- 数据结构之Treap详解
- 用C语言举例讲解数据结构中的算法复杂度结与顺序表
- C#数据结构之堆栈(Stack)实例详解
- C#数据结构之双向链表(DbLinkList)实例详解
- JavaScript数据结构和算法之图和图算法
- Java数据结构及算法实例:冒泡排序 Bubble Sort
- Java数据结构及算法实例:插入排序 Insertion Sort
- Java数据结构及算法实例:考拉兹猜想 Collatz Conjecture
- java数据结构之java实现栈
- java数据结构之实现双向链表的示例
- Java数据结构及算法实例:选择排序 Selection Sort
- Java数据结构及算法实例:朴素字符匹配 Brute Force
- Java数据结构及算法实例:汉诺塔问题 Hanoi
- Java数据结构及算法实例:快速计算二进制数中1的个数(Fast Bit Counting)