資料結構——樹狀陣列

AdviseDY發表於2024-09-17

前言:

樹狀陣列,利用二進位制亂七八糟的東西的資料結構。

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;

相關文章