【黑科技】用树状数组解决区间修改查询问题

遇到区间查询修改,相信大家一般用的都是线段树,
但常数远远没有树状数组优秀,
这里就来谈谈如何用树状数组解决区间问题
(同时可以用于多维问题)


首先,相信大家一定会用树状数组做区间加,单点查询的问题,
差分一下即可;

那么对于区间查询,怎么做呢?
要做到区间查询,就要能计算一个点的值对后面的值的影响,
多开一个数组F[i]=(n-i+1)*f[i],(f为用来差分的数组)
差分的话当然是这个点的值对后面的每个点都有影响,所以权值乘上那么多,
但很显然,在计算区间是,它的影响没有那么大,
设查询的区间是l,r,那么它在答案中应该是: ( r − i + 1 ) ∗ f [ i ] = F [ i ] − ( n − r ) ∗ f [ i ] (r-i+1)*f[i]=F[i]-(n-r)*f[i] (ri+1)f[i]=F[i](nr)f[i]
发现(n-r)对于任意的f[i]都是一定的,也就是可以区间求和以后直接乘上(n-r)减掉F多出来的部分,再加上l之前的影响,

这样既可实现用树状数组解决区间修改查询问题,

那么现在问题来了,如何用树状数组实现二维或多维的求和,
以二维为例,
对平面上的一个矩形加/减,求矩形里点的和,
我们需要多开3个数组,
F1[i][j]=(m-j+1)*(n-i+1)*f[i],
F2[i][j]=(n-i+1)*f[i],
F3[i][j]=(m-j+1)*f[i],
用矩阵和的套路,减掉右边左边,加上右下角的,即可,
对于前面的影响,与处理后面的差不多,

(这个需要大家自己感受一下,实在不会就看标吧)
(重点在Gans这个函数)

void change(int x,int y,int e)
{
	if(x>n||y>m)return;
	for(int i=x;i<=n;i+=NX(i))
		for(int j=y;j<=m;j+=NX(j))
		{
			f[i][j]+=e;
			f1[i][j]=(f1[i][j]+e*(n-x+1)*(m-y+1))%mo;
			f2[i][j]=(f2[i][j]+e*(n-x+1))%mo;
			f3[i][j]=(f3[i][j]+e*(m-y+1))%mo;
		}
}
void find(int x,int y)
{
	fd1=fd2=fd3=fd=0;
	for(int i=x;i;i-=NX(i))
		for(int j=y;j;j-=NX(j))
		{
			fd+=f[i][j];
			fd1=(fd1+f1[i][j])%mo;
			fd2=(fd2+f2[i][j])%mo;
			fd3=(fd3+f3[i][j])%mo;
		}
}
void add(int x,int y,int x1,int y1)
{
	change(x,y,1);
	change(x,y1+1,-1);
	change(x1+1,y,-1);
	change(x1+1,y1+1,1);
}
int Gans(int x,int y,int x1,int y1)
{
	LL s,s1,s2,s3;
	find(x-1,y1);s=fd,s1=fd1,s2=fd2,s3=fd3;
	find(x-1,y-1);s-=fd,s1-=fd1,s2-=fd2,s3-=fd3;
	LL t=0;
	t=(s3-s*(m-y1))%mo*(x1-x+1)%mo;
	find(x1,y-1);s=fd,s1=fd1,s2=fd2,s3=fd3;
	find(x-1,y-1);s-=fd,s1-=fd1,s2-=fd2,s3-=fd3;
	t=(t+(s2-s*(n-x1))%mo*((y1-y+1))%mo)%mo;
	find(x-1,y-1);s=fd,s1=fd1,s2=fd2,s3=fd3;
	t=(t+s*(y1-y+1)%mo*(x1-x+1))%mo;//这是前面的影响

	find(x1,y1);s=fd,s1=fd1,s2=fd2,s3=fd3;
	find(x-1,y1);s-=fd,s1-=fd1,s2-=fd2,s3-=fd3;
	find(x1,y-1);s-=fd,s1-=fd1,s2-=fd2,s3-=fd3;
	find(x-1,y-1);s+=fd,s1+=fd1,s2+=fd2,s3+=fd3;
	s1=s1-s3*(n-x1)-s2*(m-y1);s1=(s1%mo+mo)%mo;
	s=s%mo;
	s1=(s1+(n-x1)*(m-y1)%mo*s)%mo;
	return (s1+t)%mo;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值