首先考虑树状数组的一维区间修改与求和
把数列差分,那么对l和r的区间加就可以变成l位置的加和r+1的减
设差分数组为t
修改前数组为a
修改后数组为b
那么修改后的答案
ans=∑i=lrb[l]=∑i=lra[i]+b[i]∗(r−i+1)=(r+1)∑i=lra[i]+b[i]−∑i=lra[i]∗i
a[i]提前处理好,分别用两个树状数组维护 ∑ri=lb[i] 和 ∑ri=la[i]∗i
一维还是很简单的
二维略复杂一些
这次直接忽略a,式子里也不写了
树状数组中的[x,y]为[1,1]~[x,y]的结果
对于[x1,y1,x2,y2]相当于在四个角的位置分别加减
具体来说就是[x2,y2]-[x2,y1-1]-[x1-1,y2]+[x2-1,y2-1]
这是对于查询
对于修改是修改[x,y]到[n,m]的东西的
所以设x为增加值,对于修改就相当于分别做下面四个东西
[x1,y1]+x,[x1,y2+1]-x,[x2,y1+1]-x,[x2+1,y2+1]+x
ans=∑i=1x∑j=1yb[i][j]∗(x−i+1)∗(y−j+1)=(x+1)(y+1)∑i=1x∑j=1yb[i][j]−(y+1)∑i=1x∑j=1yb[i][j]∗i−(x+1)∑i=1x∑j=1yb[i][j]∗j+∑i=1x∑j=1yb[i][j]∗i∗j
那么,用四个树状数组分别存下
∑xi=1∑yj=1b[i][j]
∑xi=1∑yj=1b[i][j]∗i
∑xi=1∑yj=1b[i][j]∗j
∑xi=1∑yj=1b[i][j]∗i∗j
求值即可
代码如下
ll get(ll z,ll x,ll y)
{
ll ans=0;
for(ll i=x;i;i-=lowbit(i)) for(ll j=y;j;j-=lowbit(j)) ans+=t[z][i][j];
return ans;
}
ll sum(ll x,ll y)
{
return (((x+1)*(y+1)*get(0,x,y))%mo-((x+1)*get(2,x,y))%mo-((y+1)*get(1,x,y))%mo+get(3,x,y)+mo+mo)%mo;
}
void put(ll z,ll x,ll y,ll w)
{
for(ll i=x;i<=n;i+=lowbit(i)) for(ll j=y;j<=m;j+=lowbit(j)) t[z][i][j]+=w;
}
void ins(ll x,ll y,ll w)
{
put(0,x,y,w);
put(1,x,y,w*x);
put(2,x,y,w*y);
put(3,x,y,w*x*y);
}
int main()
{
ins(x1,y1,x);ins(x1,y2+1,-x);ins(x2+1,y1,-x);ins(x2+1,y2+1,x);//加入
ans=sum(x,y)-sum(x,y1-1)-sum(x1-1,y)+sum(x1-1,y1-1);//查询
}