数据结构——树状数组

前言:

树状数组,利用二进制乱七八糟的东西的数据结构。

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;
posted @ 2024-09-17 00:01  AdviseDY  阅读(7)  评论(0编辑  收藏  举报