• 二维树状数组学习笔记


    1 首先从一维的开始,考虑最基础的单点修改,区间查询。树状数组模板1:这个是最基础的,就是定义的应用。

    2再考虑区间修改,单点查询。树状数组模板2

    我们可以想到,在对一段区间(;[l,r];)进行修改时,可以将(;[1,r];)加上(;k),将(;[1,l-1];)减去(;k;)。但这样修改,对于单点查询来说,似乎不是那么的方便,我们可能需要求出(;[1,x];)(;[1,x-1];)的前缀和,再做个差。好像...有点麻烦。

    那么有什么更好的方法呢?——考虑差分

    我们定义(;d_i=a_i-a_{i-1},;a_0=0;)。那么每次修改,就只需要将(;d_l;)(;d_{r+1};)进行修改,而单点查询即为(;sumlimits_{i=1}^xd_i)。直接进行前缀和查询即可。

    3还有的就是区间修改,区间求和了。线段树模板1

    我们跟着上面区间修改,单点查询的思路来,还是考虑差分。

    修改的话,如同上面一样,只对差分数组(;d_{l-1};)(;d_r;)进行修改,但有些许变动。

    我们来看一个式子:

    [sumlimits_{i=1}^rsumlimits_{j=1}^id_j-sumlimits_{i=1}^{l-1}sumlimits_{j=1}^id_j ]

    这个式子前一半表示的是区间(;[1,r];)的和,后一半是区间(;[1,l-1];)的和,二者做差即为所求的区间(;[l,r];)的和。

    尝试对其化简。

    我们先只考虑前一半,即(;sumlimits_{i=1}^rsumlimits_{j=1}^id_j)

    发现(;d_1;)出现了(;r;)次,(;d_2;)出现了(;r-1;)(;ldots) (d_x;)出现了(;r-x+1;)次。至此,我们发现式子可以变为:

    [sumlimits_{i=1}^rd_i imes (r-i+1) ]

    展开可得:

    [(r+1) imes sumlimits_{i=1}^rd_i-sumlimits_{i=1}^rd_i imes i ]

    最终我们只需开两个树状数组来维护(;d_i;)(;d_i imes i;)即可。

    修改:

    il void add(int x,ll val)
    {
    	for(int i=x;i<=n;i+=lowbit(i))
    		c1[i]+=val,c2[i]+=x*val;
    }
    il void real_add(int l,int r,int val)
    {
    	add(l,val);
    	add(r+1,-val);
    }
    

    查询:

    il ll ask(int x)
    {
      ll ans=0;
      for(int i=x;i;i-=lowbit(i))
      	ans+=c1[i]*(x+1)-c2[i];
      return ans;
    }
    il ll real_ask(int l,int r)
    {
      return ask(r)-ask(l-1);
    }
    

    4接下来考虑单点修改,区间查询。二维树状数组模板

    我们只需将一维扩展到二维,代码只需进行一定的修改即可:

    修改:

    inline void add(int x,int y,int val)
    {
    	for(int i=x;i<=n;i+=lowbit(i))
    		for(int j=y;j<=m;j+=lowbit(j))
    			a[i][j]+=val;
    }
    

    查询:

    inline int ask(int x,int y)
    {
    	int ans=0;
    	for(int i=x;i;i-=lowbit(i))
    		for(int j=y;j;j-=lowbit(j))
    			asn+=a[i][j];
    	return ans;
    }
    

    5最后是区间修改,区间查询。上帝造题的七分钟

    回想一下,我们是怎么进行一维的区间修改的——差分。差分数组(;sumlimits_{i=1}^nd_i=a_i;)。再想一下差分数组是如何求出来的。

    我们来看两个式子:

    [sum_i=sum_{i-1}+a_i ]

    [d_i=a_i-a_{i-1} ]

    应该能发现什么吧——差分数组的计算有点像前缀和计算的逆运算。所以,我们可以仿照二维数组的前缀和公式来推出二维数组的差分公式。

    [sum_{ij}=sum_{{i-1}j}+sum_{i{j-1}}-sum_{{i-1}{j-1}} ]

    [d_{ij}=a_{ij}-a_{{i-1}j}-a_{i{j-1}}+a_{{i-1}{j-1}} ]

    在回想一下,我们在进行一维数组的差分修改时是如何修改的——再将其扩展到二维。

    当我们要修改一个二维区间 (;(x_1,y_1) (x_2,y_2);) 所组成的矩形时,只需修改(;(x_1,y_1) (x_1,y_2+1) (x_2+1,y_1) (x_2+1,y_2+1);)四个点。那么,如何修改呢?

    我们来看一个式子:

    [sumlimits_{i=1}^xsumlimits_{j=1}^ysumlimits_{p=1}^isumlimits_{q=1}^jd_{pq} ]

    我们发现,(;d_{11};) 出现了(;x imes y;)次,(;d_{12};)出现了 (;x imes {(y-1)};)(;ldots) (;d_{pq};)出现了(;{(x-p+1)} imes {(y-q+1)};)次。

    我们将式子展开,可得到

    [sumlimits_{i=1}^xsumlimits_{j=1}^y{d_{ij} imes (x-i+1) imes (y-j+1)} ]

    再次展开

    [{(x+1) imes (y+1) imes sumlimits_{i=1}^xsumlimits_{j=1}^yd{ij}}-{(y+1) imes sumlimits_{i=1}^xsumlimits_{j=1}^y{d{ij} imes i}}-{(x+1) imes sumlimits_{i=1}^xsumlimits_{j=1}^y{d{ij} imes j}}+{sumlimits_{i=1}^xsumlimits_{j=1}^y{d{ij} imes i imes j}} ]

    那么,我们就只需开四个树状数组来分别维护(d_{ij}quad d_{ij} imes iquad d{ij} imes jquad d{ij} imes i imes j)即可。

    区间查询就是(;sum_{{x_2}{y_2}}-sum_{{x_2}{y_1-1}}-sum_{{x_1-1}{y_2}}+sum_{{x_1-1}{y_1-1}})

    完整代码:

    //2020.11.4
    //二维树状数组,区间修改,区间查询
    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=2050;
    #define NEKO puts("NEKO")
    #define il inline
    #define vocaloid(v) (v>='0'&&v<='9')
    il int read()
    {
       int x=0,flag=1;char v=getchar();
       while(!vocaloid(v)) {if(v=='-') flag=-1;v=getchar();}
       while(vocaloid(v)) {x=(x<<1)+(x<<3)+v-'0';v=getchar();}
       return x*flag;
    }
    il int lowbit(int x) {return x&(-x);}
    int n,m,c1[maxn][maxn],c2[maxn][maxn],c3[maxn][maxn],c4[maxn][maxn];
    char ch;int X1,X2,Y1,Y2,val;
    il void add(int x,int y,int val)
    {
       for(int i=x;i<=n;i+=lowbit(i))
       	for(int j=y;j<=m;j+=lowbit(j))
       	{
       		c1[i][j]+=val;
       		c2[i][j]+=val*x;
       		c3[i][j]+=val*y;
       		c4[i][j]+=val*x*y;
       	} 
    }
    il void real_add(int X1,int Y1,int X2,int Y2,int val)
    {
       add(X1,Y1,val);
       add(X1,Y2+1,-val);
       add(X2+1,Y1,-val);
       add(X2+1,Y2+1,val);
    }
    il int ask(int x,int y)
    {	
       int ans=0;
       for(int i=x;i>0;i-=lowbit(i))
       	for(int j=y;j>0;j-=lowbit(j))
       		ans+=(x+1)*(y+1)*c1[i][j]-(y+1)*c2[i][j]-(x+1)*c3[i][j]+c4[i][j];
       return ans;
    }
    il int real_ask(int X1,int Y1,int X2,int Y2)
    {
       return ask(X2,Y2)-ask(X2,Y1-1)-ask(X1-1,Y2)+ask(X1-1,Y1-1);
    }
    int main()
    {
       cin>>ch;n=read(),m=read();
       while(cin>>ch)
       {
       	if(ch=='L')
       	{
       		X1=read(),Y1=read(),X2=read(),Y2=read(),val=read();
       		real_add(X1,Y1,X2,Y2,val);
       	}
       	if(ch=='k')
       	{
       		X1=read(),Y1=read(),X2=read(),Y2=read();
       		printf("%d
    ",real_ask(X1,Y1,X2,Y2));
       	}
       }
       return 0;
    }
    

    (PS) 部分内容参考自以下几篇博客:

    Lv1_kangdi——二维树状数组总结及模板

    fmj_123——luogu-P4514-二维树状数组

    十分感谢他们的博客对我学习此内容的帮助。

  • 相关阅读:
    BI 和报表有什么区别
    软件评测师考试总复习(可靠性测试)
    软件评测师考试总复习(易用性测试)
    软件评测师考试总复习(标准符合性测试)
    软件评测师考试总复习(兼容性测试)
    软件评测师考试总复习(安全性测试与评估)
    软件评测师考试总复习(网络测试)
    软件评测师考试总复习(软件测试)
    软件评测师考试总复习(数据库技术)
    软件评测师考试总复习(WEB应用测试)
  • 原文地址:https://www.cnblogs.com/MIKU5201314/p/14086661.html
Copyright © 2020-2023  润新知