维护一些满足结合律又满足结合律的群,也就是交换群,与线段树不同,线段树只需要满足结合律,是幺半群。
原因在于线段树的区间操作是真正的从小区间合并上来的,但是树状数组是来源于两个前缀和作差,这就要求这个“前缀和”拥有逆元,也就是“减法”。
这样,拥有逆元运算的就可以使用树状数组维护。
单点加值,区间查询:
初始化的时候手动把每一个值add上去就可以了。
const int MAXN=500000; int a[MAXN+5]; int c[MAXN+5]; int n; inline int lowbit(int x) { return (x & -x); } //a[x]+=k void add(int x, int k) { for(int i=x;i<=n;i+=lowbit(i)) c[i]+=k; } int get_sum(int x) { int ans = 0; for(int i=x;i;i-=lowbit(i)) ans+=c[i]; return ans; } inline int range_query(int l,int r) { return get_sum(r)-get_sum(l-1); }
求逆序对:
好像是求一整个数组的逆序对,留坑。
求区间最大/最小值:
这个是个假的区间最大/最小值,这个只能求[1,n],没啥用,不写了。
区间加值,单点查询:
利用差分的思想很容易实现,初始化的时候也可以每一个值手动add上去。
const int MAXN=500000; int a[MAXN+5]; int c[MAXN+5]; int n; inline int lowbit(int x) { return (x & -x); } void add(int x, int k) { for(int i=x;i<=n;i+=lowbit(i)) c[i]+=k; } void range_add(int l,int r,int k){ add(l,k),add(r+1,-k); } int get_sum(int x) { int ans = 0; for(int i=x;i;i-=lowbit(i)) ans+=c[i]; return ans; } inline int query(int x) { return get_sum(x); }
示例:
记得是从1开始的。
#include<bits/stdc++.h> using namespace std; #define ll long long const int MAXN=500000; int a[MAXN+5]; int c[MAXN+5]; int n; inline int lowbit(int x) { return (x & -x); } //a[x]+=k void add(int x, int k) { for(int i=x;i<=n;i+=lowbit(i)) c[i]+=k; } int get_sum(int x) { int ans = 0; for(int i=x;i;i-=lowbit(i)) ans+=c[i]; return ans; } inline int range_query(int l,int r) { return get_sum(r)-get_sum(l-1); } int main() { int m; while(~scanf("%d%d",&n,&m)){ memset(a,0,sizeof(a)); memset(c,0,sizeof(c)); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); add(i,a[i]); } while(m--){ int ins; scanf("%d",&ins); if(ins==1){ int x,k; scanf("%d%d",&x,&k); add(x,k); } else{ int x,y; scanf("%d%d",&x,&y); printf("%d ",range_query(x,y)); } } } }
区间加值,区间查询:
考虑上面的区间加值,单点查询,我们要求的其实是query的前缀和,那么每个差分被用的次数是递减的。维护一个次数递增的差分和次数不变的差分,作差可以获得次数递减的差分。
const int MAXN=500000; int a[MAXN+5]; int c[MAXN+5]; int c2[MAXN+5]; int n; inline int lowbit(int x) { return (x & -x); } void add(int x, int k) { for(int i=x;i<=n;i+=lowbit(i)) c[i]+=k,c2[i]+=k*x; } void range_add(int l,int r,int k){ add(l,k),add(r+1,-k); } int get_sum(int x) { int ans = 0; for(int i=x;i;i-=lowbit(i)) ans+=(x+1)*c[i]-c2[i]; return ans; } inline int query(int l,int r) { return get_sum(r)-get_sum(l-1); }
二维树状数组:
留坑。