概念
参考博客:http://www.cnblogs.com/George1994/p/7710886.html
树状数组或者二叉索引树也称作Binary Indexed Tree,又叫做Fenwick树;它的查询和修改的时间复杂度都是log(n)
,空间复杂度则为O(n)
,这是因为树状数组通过将线性结构转化成树状结构,从而进行跳跃式扫描。通常使用在高效的计算数列的前缀和,区间和。
lowbit
它通过公式来得出k,其中k就是该值从末尾开始0的个数。然后将其得出的结果加上x自身就可以得出当前节点的父亲节点的位置或者是x减去其结果就可以得出上一个父亲节点的位置。比如当前是6,二进制就是0110,k为2,那么6+2=8,而C(8)则是C(6)的父亲节点的位置;相反,6-2=4,则是C(6)的上一个父亲节点的位置。
def LOWBIT(x): return x & (-x)
注意:LOWBIT无法处理0的情况,因为它的结果也是0,那么最终就是一个死循环
单点修改
当我们要对最底层的值进行更新时,那么它相应的父亲节点存储的和也需要进行更新,所以修改的代码如下:
def MODIFY(x, delta): if x < 1: return while x <= n: fenwick[x] += delta x += LOWBIT(x)
查询
而查询的时候,则需要向前进行统计
def QUERY(x): result = 0 while right > 0: result += fenwick[x] x -= LOWBIT(x) return result
题目:https://www.nowcoder.com/acm/contest/139/J
题意:给出n个数,求 [1,L],[R,n]这两个区间不同数的个数
其实你只要把区间扩大一倍,就是求 [R,L+n]这个区间了
#include<bits/stdc++.h> using namespace std; #define MAX 200005 int bit[MAX],n,q; map<int,int>mp; struct Query { int l,r,id; bool operator < (const Query& a)const{ return r<a.r; } }b[MAX]; int gsum(int i) { int s=0; while(i>0) { s+=bit[i]; i-=i&-i; } return s; } void add(int i,int k) { while(i<=n) { bit[i]+=k; i+=i&-i; } } int main() { while(~scanf("%d %d",&n,&q)){ mp.clear(); memset(bit,0,sizeof(bit)); vector<int>a(n*2+10),ans(q); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); a[i+n]=a[i]; } n=n*2; for(int i=0;i<q;i++) { int l,r; scanf("%d %d",&l,&r); b[i].l=r; b[i].r=l+n/2; b[i].id=i; } sort(b,b+q); int pre=1; for(int i=0;i<q;i++) { for(int j=pre;j<=b[i].r;j++) { if(mp[a[j]]) { add(mp[a[j]],-1); } add(j,1); mp[a[j]]=j; } pre=b[i].r+1; ans[b[i].id]=gsum(b[i].r)-gsum(b[i].l-1); } for(int i=0;i<q;i++) { printf("%d ",ans[i]); } } }
【进阶——树状数组】 区间求最值
参考博客:https://www.cnblogs.com/mypride/p/5002556.html