【模板篇】树状数组(六)

上次我们聊到了二维树状数组的 单点加 区间查.
那么我们yy一下区间加 单点查也不难.
我们就让树状数组维护差分数组, 只不过是变成了二维差分, 都差不多的.
我们可以仿照一维的 改写成这样

int c[N][N],n,m;
void add(int x,int y,int s){
	for(int i=x;i<=n;i+=i&-i)
		for(int j=y;j<=m;j+=j&-j)
			c[i][j]+=s;
}
int query(int x,int y,int s=0){
	for(int i=x;i;i-=i&-i)
		for(int j=y;j;j-=i&-j)
			s+=c[i][j];
}
void Add(int x1,int y1,int x2,int y2,int s){
	add(x1,y1,s); add(x1,y2+1,-s); add(x2+1,y1,-s); add(x2+1,y2+1,s);
}

而其实二维树状数组也可以做到区间加, 区间查的.
而且据说线段树套线段树是没法维护的....
我们看一道板子题: 上帝造题的七分钟

那怎么做呢? 我们还是仿照一维的推柿子.
不过今天我按照之前的方法推柿子感觉并不好推, 于是获取了一种新的推法.
由于我们维护的差分数组的前缀和才是单点的值, 也就是说我们计算\(a_i\)时, \(c_1\sim c_i\)每个值都会被计算一次.
这样我们计算\(\sum_{i=1}^na_i\)的时候,\(c_1\)应该会出现\(n\)次, \(c_2\)应该会出现\(n-1\)次, ... , \(c_k\)会出现\(n-k+1\)次, ... ,\(c_n\)会出现一次. 所以

\[ans=\sum_{i=1}^na_i=\sum_{i=1}^n\sum_{j=1}^ic_j=\sum_{i=1}^nc_i*(n-i+1)=\sum_{i=1}^nc_i*(n+1)-\sum_{i=1}^nc_i*i \]

这样我们用两个树状数组分别维护\(c_i\)的和和\(c_i*i\)的和即可.
而之前我们得出的结论也不是错的, 如果我们这么拆

\[ans=\sum_{i=1}^nc_i*(n-i+1)=\sum_{i=1}^nc_i*n+\sum_{i=1}^nc_i*(i-1) \]

就变成了维护\(c_I\)\(c_i*(i-1)\), 最后结果应该是一样的, 具体用哪种看个人喜好吧... (似乎前面那种用起来比较舒服..)


情况扩展到了二维, 除了二维差分复杂了一(hen)些(duo)以外, 原理上是一样的.
我们依然用\(c_{i,j}\)维护差分数组, 所以显然\(\sum_{i=1}^n\sum_{j=1}^mc_{i,j}=a_{n,m}\)
然后仿照一维的, 把一个查询拆成四个, 对于每个查询,

\[ans=\sum_{i=1}^n\sum_{j=1}^m\sum_{x=1}^i\sum_{y=1}^jc_{i.j} \]

这里面很显然每个\(c_i,j\)被使用的次数等于\((i,j)\)为左上角,\((n,m)\)为右下角的矩形面积(不信你可以画个图试试), 也就是\((n-i+1)*(m-j+1)\), 所以

\[ans=\sum_{i=1}^n\sum_{j=1}^m\sum_{x=1}^i\sum_{y=1}^jc_{i.j}=\sum_{i=1}^n\sum_{j=1}^mc_{i.j}*(n-i+1)*(m-j+1) \\ =\sum_{i=1}^n\sum_{j=1}^mc_{i,j}*(n+1)*(m+1)-\sum_{i=1}^n\sum_{j=1}^mc_{i,j}*i*(m+1)-\sum_{i=1}^n\sum_{j=1}^mc_{i,j}*j*(n+1)+\sum_{i=1}^n\sum_{j=1}^mc_{i,j}*i*j \]

所以按照套路我们只需要开四个树状数组分别维护\(c_{i,j},c_{i,j}*i,c_{i,j}*j,c_{i,j}*i*j\)就行了..

不管是区间加还是区间查都巨繁琐qwq...
下面给出代码;

#include <cstdio>
inline int gn(int a=0,char c=0,int f=1){
	for(;(c<'0'||c>'9')&&c!='-';c=getchar());
	if(c=='-') c=getchar(),f=-1;
	for(;c>47&&c<58;c=getchar()) a=a*10+c-48;
return a*f;}
struct BIT{
	int c[2050][2050],n,m;
	
	void add(int x,int y,int s){
		for(int i=x;i<=n;i+=i&-i)
			for(int j=y;j<=m;j+=j&-j)
				c[i][j]+=s;
	}
	
	int query(int x,int y,int s=0){
		for(int i=x;i;i-=i&-i)
			for(int j=y;j;j-=j&-j)
				s+=c[i][j];
		return s;
	}
	
}a,b,c,d;

void Add(int x,int y,int s){
	a.add(x,y,s); b.add(x,y,s*x); c.add(x,y,s*y); d.add(x,y,s*x*y);
}
int Query(int x,int y){
	return a.query(x,y)*(x+1)*(y+1)-b.query(x,y)*(y+1)-c.query(x,y)*(x+1)+d.query(x,y); 
}

int main(){
	int n=gn(),m=gn(); char opt[4];
	a.n=b.n=c.n=d.n=n; a.m=b.m=c.m=d.m=m;
	while(~scanf("%s",opt)){
		if(opt[0]=='L'){
			int x1=gn(),y1=gn(),x2=gn(),y2=gn(),s=gn();
			Add(x1,y1,s); Add(x1,y2+1,-s); Add(x2+1,y1,-s); Add(x2+1,y2+1,s); 
		}
		else{
			int x1=gn(),y1=gn(),x2=gn(),y2=gn();
			printf("%d\n",Query(x2,y2)-Query(x1-1,y2)-Query(x2,y1-1)+Query(x1-1,y1-1));
		}
	}
} 
posted @ 2018-04-28 15:29  Enzymii  阅读(165)  评论(0编辑  收藏  举报