二维树状数组

二维树状数组

本篇博客旨在实现一种玄妙的黑科技:二维树状数组,支持区间修改和区间查询
虽然这个东西叫做树状数组,但其实和普通树状数组除了\(lowbit\)之外大约没什么特别相似的东西……
我们考虑,普通的树状数组中,我们用\(t[x]\)维护了右端点为\(x\),长度为\(lowbit(x)\)的区间的和,那么同理,在二维树状数组中,我们用\(t[x][y]\)维护左上角顶点为\((x,y)\),大小为\(lowbit(x)*lowbit(y)\)的区间的和
我们知道,树状数组的区间操作实际上依托于差分和前缀和。那么同理,我们尝试利用二维差分和前缀和来维护二维树状数组
具体过程如下:

区间修改

我们回忆一下我们是如何对一维树状数组进行区间修改的。我们对其进行差分操作,是为了使得到的差分数组前缀和就等于对应位置元素的值。那么我们可以运用类比思想,设计一个二维的差分
我们知道二维前缀和具有计算公式:
\(sum[i][j]=sum[i−1][j]+sum[i][j−1]−sum[i−1][j−1]+a[i][j]\)
我们又知道差分是前缀和的逆运算,那么我们可以得到对于区间加,存在差分操作:
假设需要修改的区间为\([(x1,y1),(x2,y2)]\),则
\(t[x1][y1]+k,t[x2+1][y2+1]+k,t[x1][y2+1]+k,t[x2+1][y1]+k\)

区间查询

根据差分数组的定义,我们不难发现,对于点\((x,y)\),它的二维前缀和就是:
\(\sum\limits_{i=1}^{x}\sum\limits_{j=1}^{y}\sum\limits_{h=1}^{i}\sum\limits_{k=1}^{j}t[h][k]\)
但我们类比一下一维树状数组的区间求和,我们亦可以统计每个\(d[h][k]\)出现的次数,我们就可以发现\(d[1][1]\)出现了\((x×y)\)次,\(d[1][2]\)出现了\(x×(y−1)\)次……\(d[h][k]\)出现了\((x−h+1)\times(y−k+1)\)
则原式整理得:
\(\sum\limits_{i=1}^{x}\sum\limits_{j=1}^{y}t[h][k]\times(x-i+1)\times(y-j+1)\)
分解并整理,得到:
\(\sum\limits_{i=1}^{x}\sum\limits_{j=1}^{y}(t[h][k]\times(xy+x+y+1)-t[h][k]\times{i}\times(y+1)-t[h][k]\times{j}\times(x+1))+t[h][k]\times{i}\times{j}\)
根据我们最后分解出来的公式,我们需要维护四个数组\(d[i][j],d[i][j]\times{i},d[i][j]\times{j},d[i][j]\times{i}\times{j}\),从而实现区间查询

例题 P4514 上帝造题的七分钟

朴素二维树状数组,支持操作区间加、区间查询。代码如下:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define N 3000
#define M 200010
#define lowbit(x) x&(-x) 

int n,m,a,b,c,d,k;
char op[2];

/*二维树状数组模板*/

struct BIT{
	int t[N][N];
	
	void add(int xi,int xj,int k){
		for(int i=xi;i<=n;i+=lowbit(i)){
			for(int j=xj;j<=m;j+=lowbit(j)){
				t[i][j]+=k;
			}
		}
		return;
	}
	
	int ask(int xi,int xj){
		int sum=0;
		for(int i=xi;i>=1;i-=lowbit(i)){
			for(int j=xj;j>=1;j-=lowbit(j)){
				sum+=t[i][j];
			}
		}
		return sum;
	}
}T[4];	

void Add(int x,int y,int k){
	T[0].add(x,y,k);
	T[1].add(x,y,k*x);
	T[2].add(x,y,k*y);
	T[3].add(x,y,k*x*y);
}

void modify(int a,int b,int c,int d,int k){
	Add(a,b,k);
	Add(c+1,b,-k);
	Add(a,d+1,-k);
	Add(c+1,d+1,k);
	//二维差分 
}	

int Ask(int x,int y){
	int res=0;
	res+=T[0].ask(x,y)*(x*y+x+y+1);
	res-=T[1].ask(x,y)*(y+1);
	res-=T[2].ask(x,y)*(x+1);
	res+=T[3].ask(x,y);
	return res;
}

int query(int a,int b,int c,int d){
	int res=0;
	res+=Ask(c,d);
	res+=Ask(a-1,b-1);
	res-=Ask(c,b-1);
	res-=Ask(a-1,d);
	return res;
	//二维前缀和 
}

signed main(){
	scanf("X %d %d",&n,&m);
	while(~scanf("%s",&op)){
		scanf("%d%d%d%d",&a,&b,&c,&d);
		if(op[0]=='L'){
			scanf("%d",&k);
			modify(a,b,c,d,k);
		}else{
			printf("%d\n",query(a,b,c,d));
		}
	}
	return 0;
}

posted @ 2025-05-15 16:32  Yun_Mo_s5_013  阅读(50)  评论(0)    收藏  举报