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


    以前一直以为树状数组只能处理单点查询和区间修改、单点修改和区间查询的问题。最近看到两个代码,发现自己太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;
    }
    

      

  • 相关阅读:
    堪称为经典游戏设计帖整理20个点击回复超高的精品贴
    【专题报道】Google I/O开发者大会
    android用户界面之AlarmManager教程实例汇
    Android2.2 API 中文文档系列
    ruby设计模式之【观察者】模式2————更加一般化的观察者模式
    ruby中require和load的区别
    ruby/python/java全覆盖的SeleniumWebdriver系列教程(1)————快速开始
    ruby + nokogiri实现将天涯易读全帖转换成txt文件的功能
    WatirWebdriver与watir1.x的差异
    Notepad++ 的一些常用快捷键
  • 原文地址:https://www.cnblogs.com/hefenghhhh/p/6554594.html
Copyright © 2020-2023  润新知