树状数组处理区间查询和区间修改的问题

以前一直以为树状数组只能处理单点查询和区间修改、单点修改和区间查询的问题。最近看到两个代码,发现自己太out了。它还能处理区间查询和区间修改(加减一个数)的问题。我们先来看一维的情况。

对$[s,t]$区间增加$x$,利用差分的思想,只需要让$delta[s]+x,delta[t+1]-x$即可。其中$delta$表示差分数组。

查询区间$[s,t]$的和,可以通过区间$[1,t]$-$[1,s-1]$得来。而$sum(1,t)=sum_{i=1}^t delta[i]*(t-i+1)$,$sum(1,s-1)=sum_{i=1}^{s-1}delta[i]*(s-i)$

于是$sum[s,t]=sum[1,t]-sum[1,s-1]=sum_{i=1}^{t}delta[i]*(t+1)-sum_{i=1}^{s-1}delta[i]*s-sum_{i=s}^t(delta[i]*i)$

其中$num'$表示修改过后的数组,$num$表示原数组,$delta$表示差分数组。

我们用两个树状数组,一个维护$delta[i]$,一个维护$delta[i]*i$,那么上面的区间修改和区间查询均可以在$O(logN)$的时间内完成了。

再看两维的情况:

对区间$[x1,y1,x2,y2]$增加x,同样利用差分的思想,只需要让$delta[x1][y1]+x,delta[x1,y2+1]-x,delta[x2,y1+1]-x,delta[x2+1][y2+1]+x$即可。同样,$delta[x][y]$表示从点$[x,y]$到点$[n,m]$的区域的增量。

定义$[x1,y1,x2,y2]$表示左下角顶点为$[x1,y1]$,右上角顶点为$[x2,y2]$的区域,该区域的数之和用$sum(x1,y1,x2,y2)$表示。可得$sum(x1,y1,x2,y2)=sum(1,1,x2,y2)-sum(1,1,x2,y1-1)-sum(1,1,x1-1,y2)+sum(1,1,x1-1,y1-1)$.

如何求$sum(1,1,x2,y2)$呢?

$sum(1,1,x2,y2)=sumlimits_{i=1}^{x2}sumlimits_{j=1}^{y2}(sumlimits_{i'=1}^{i} sumlimits_{j'=1}^j delta[i'][j'])=sumlimits_{i=1}^{x2} sumlimits_{j=1}^{y2} delta[i][j]*(x2-i+1)*(y2-j+1)$

对$num$求和可以使用前缀和预处理,而对delta数组求和

$sumlimits_{i=1}^{x2} sumlimits_{j=1}^{y2} delta[i][j]*(x2-i+1)*(y2-j+1)=sumlimits_{i=1}^{x2} sumlimits_{j=1}^{y2} delta[i][j]*((x2+1)(y2+1)-(x2+1)*j-(y2+1)*i+i*j)\=(x2+1)*(y2+1)*sumlimits_{i=1}^{x2}sumlimits_{j=1}^{y2}delta[i][j]\-(x2+1)*sumlimits_{i=1}^{x2} sumlimits_{j=1}^{y2}(j*delta[i][j])\-(y2+1)*sumlimits_{i=1}^{x2} sumlimits_{j=1}^{y2}(i*delta[i][j])\+sumlimits_{i=1}^{x2} sumlimits_{j=1}^{y2}delta[i][j]*i*j$

上式最终可以分为四个树状数组求和,四个树状数组分别维护$delta[i][j]$,$delta[i][j]*i$,$delta[i][j]*j$,$delta[i][j]*i*j$.

下面是用二维树状数组完成二维区间查询和二维区间修改的操作。

#include <iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
#define MAXN 2055
int x1,y1,x2,y2,n,m,val;
char opt[3];
int tree[5][MAXN][MAXN];
#define lowbit(x) (x&-(x))
void update(int w,int posx,int posy,int val)
{
  for(int i=posx;i<=n;i+=lowbit(i))
  {
      for(int j=posy;j<=m;j+=lowbit(j))
     {
         tree[w][i][j]+=val;
     }
  }
}
int getsum(int w,int posx,int posy)
{
    int sum=0;
  for(int i=posx;i;i-=lowbit(i))
  {
      for(int j=posy;j;j-=lowbit(j))
     {
         sum+=tree[w][i][j];
     }
  }
  return sum;
}
void up(int a,int b,int val)
{
    update(0,a,b,val);
    update(1,a,b,b*val);
    update(2,a,b,a*val);
    update(3,a,b,a*b*val);
}
int sum(int a,int b)
{
    return (a+1)*(b+1)*getsum(0,a,b)-(b+1)*getsum(2,a,b)-(a+1)*getsum(1,a,b)+getsum(3,a,b);
}
int main()
{
    scanf("%s%d%d",opt,&n,&m);
    while(scanf("%s",opt)!=-1)
    {
        if(opt[0]=='L')
        {
            scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&val);
            up(x1,y1,val);
            up(x1,y2+1,-val);
            up(x2+1,y1,-val);
            up(x2+1,y2+1,val);
        }
        else
        {
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            printf("%d",sum(x2,y2)-sum(x2,y1-1)-sum(x1-1,y2)+sum(x1-1,y1-1));
        }
    }
    printf("
");
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            printf("%d ",tree[0][i][j]);
            if(j==m)printf("
");
            else printf(" ");
        }
            printf("
");


    return 0;
}

  

原文地址:https://www.cnblogs.com/hefenghhhh/p/6554594.html