前言:
树状数组,利用二进制乱七八糟的东西的数据结构。
trick:
修改,查询:
区间修改和单点修改分开来说,区间修改相当于树状数组维护的是一个差分数组,区间修改对应的单点查询,单点修改对应的区间查询,前者是维护差分数组,后者维护的是原本的数组,这次不过多赘述,因为如果树状数组是以下标进行维护的话,那本人更习惯使用线段树。大多数还是权值树状数组用的更多一些。
查询第k大(小)值:
(来自https://www.cnblogs.com/LiuRunky/p/BIT_Find_kth.html)
如图所示,由于树状数组的特性,最高位的1所在的位置会存储到所有数字的个数。所以我们可以像倍增那样,i从大往小,设当前位置是x,则如果F[x+i]<k,那么就进行下去。进行下去的话,x+=i。接着继续。
int find(int k){int x=0,sum=0;if(qry(0,n)<k || k<=0) return -114514;for(int i=1<<std::__lg(n);i;i/=2){//这里是小于而没有等于号是因为我们要找到最右边小于k的数然后再+1才是我们要的答案,但是又因为这里树状数组整体下标+1了已经(因为这样可以解决下标0)所以直接return x。if(x+i<=n and sum+F[x+i]<k){sum+=F[x+i];x+=i;}}return x;}
二维查询k大:
倘若我们有一个矩阵,要查询这个矩阵横坐标在l,r之间的k大数该怎么办呢?我们可以用到二维树状数组。
直接贴代码先:
int suan(int x,int i){int sum=0;for(int q=x;q>=1;q-=fan(q)) sum+=F[q][i];return sum;
}
int find(int l,int r,int k){l+=1,r+=1;int y=0,sum=0;for(int i=1<<std::__lg(m);i;i/=2){int tem=suan(r,y+i)-suan(l-1,y+i);if(y+i<=m and sum + tem < k){sum+=tem;y+=i;}}return y;
}
用举例去说明,我们知道最高位的1所在的位置会存储到所有数字的个数。但是二维的怎么做呢?我们总不能直接用\(F[r][i]-F[l-1][i]\),因为我们需要表示前r行里前i个的数,第二维是能够直接代表第r行的所有数字,但是第一维的r因为lowbit不是0,所以我们不能直接使用,需要qry,所以我们要用suan()函数处理出来,对r进行qry,l也是同理。
模板:
一维树状数组:
class tree {vector<int> F;int n;int fan(int x) { return x & -x; }int get(int x) {x += 1; int tem = 0; for (int i = x; i >= 1; i -= fan(i)) tem += F[i];return tem;}
public:void init(int n_) {vector<int>().swap(F);n=n_+1;F.resize(n + 5);}void add(int x, int val) {x += 1; for (int i = x; i <=n; i += fan(i)) F[i] += val;}int qry(int l, int r) {if (l > r) return 0; if (l - 1 < 0) return get(r);return get(r) - get(l - 1);}int find(int k){int x=0,sum=0;if(qry(0,n)<k || k<=0) return -114514;for(int i=1<<std::__lg(n);i;i/=2){if(x+i<=n and sum+F[x+i]<k){sum+=F[x+i];x+=i;}}return x;}
}shu;
二维树状数组:
class tree {vector<vector<int> > F;int n,m;int fan(int x) { return x & -x; }int get(int x,int y){x+=1,y+=1;int tem=0;for(int i=x;i>=1;i-=fan(i))for(int j=y;j>=1;j-=fan(j)){tem+=F[i][j];}return tem;}
public:void init(int n_,int m_) {n=n_+1,m=m_+1;F.reserve(n+10);rep(i,1,n) F[i].assign(m+10,0);}void add(int x,int y, int val) {x+=1,y+=1;for(int i=x;i<=n;i+=fan(i)){for(int j=y;j<=m;j+=fan(j)){F[i][j]+=val;}}}int qry(int x1, int y1,int x2,int y2) {return get(x2,y2)-get(x1-1,y2)-get(x2,y1-1)+get(x1-1,y1-1);}
}shu;