遇到区间查询修改,相信大家一般用的都是线段树,
但常数远远没有树状数组优秀,
这里就来谈谈如何用树状数组解决区间问题
(同时可以用于多维问题)
首先,相信大家一定会用树状数组做区间加,单点查询的问题,
差分一下即可;
那么对于区间查询,怎么做呢?
要做到区间查询,就要能计算一个点的值对后面的值的影响,
多开一个数组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]
(r−i+1)∗f[i]=F[i]−(n−r)∗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;
}