二维树状数组 学习笔记

前言:遇到二维的问题都很虚,而且树状数组也不熟练……于是学了一发这个。

------------------------------

以下所有问题均在二维中

1.单点修改,单点查询

这个最简单,直接开一个二维数组搞一搞就完事了。

2.单点修改,区间查询

回想一下一维的树状数组是怎么搞的:我们维护序列的前缀和;查询区间的时候直接让前缀和相减就好。放到二维也是同理。给出区间查询的式子:

$ans=sum(x2,y2)-sum(x1-1,y2)-sum(x2,y1-1)+sum(x1-1,y1-1)$

代码:

inline void add(int x,int y,int k)
{
    for (int i=x;i<=n;i+=lowbit(i))
        for (int j=y;j<=m;j+=lowbit(y))
            tree[x][y]+=k;
}
inline int sum(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+=tree[x][y];
    return ans;
}

3.区间修改,单点查询

回想一下一维的树状数组是怎么维护的:我们维护$a[i]$的差分数组$d[i]$;让区间$[l,r]$加$k$就让$d[l]$加$k$,$d[r+1]$减$k$。求和就是$a[i]=\sum\limits_{j=1}^i d[j]$。放到二维也是一样,差分数组的意义变成如下:

$d[i][j]=a[i][j]-a[i-1][j]-a[i][j-1]+a[i-1][j-1]$(类比二维前缀和)

比如我们想让$(2,2,3,4)$之间的部分加$1$:

原先是这样的:

0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0

做一下差分:

0  0  0  0  0
0  1  0  0  -1
0  0  0  0  0
0 -1  0  0  1

代码:

inline void add(int x,int y,int k)
{
    for (int i=x;i<=n;i+=lowbit(i))
        for (int j=y;j<=m;j+=lowbit(y))
            tree[x][y]+=k;
}
inline int sum(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+=tree[x][y];
    return ans;
}
inline void update(int x1,int y1,int x2,in y2)
{
    add(x1,y1,1);
    add(x2+1,y1,-1);
    add(x1,y2+1,-1);
    add(x2+1,y2+1,1);
}

4.区间修改,区间查询

这个我一维树状数组也没干过这个,都是拿线段树写的……直接推式子吧。

我们还是维护差分数组。想求$(1,1,x,y)$内所有数和,我们有:

$ans=\sum\limits_{i=1}^x \sum\limits_{j=1}^y \sum\limits_{k=1}^i \sum\limits_{h=1}^j d[k][h]$

考虑每个$d$算了多少次,我们可以化简得到:

$ans=\sum\limits_{i=1}^x \sum\limits_{j=1}^y d[i][j]*(x-i+1)*(y-j+1)$\

$ans=(x+1)*(y+1)\sum\limits_{i=1}^x \sum\limits_{j=1}^y d[i][j]-(x+1)\sum\limits_{i=1}^x \sum\limits_{j=1}^y d[i][j]*j-(y+1)*\sum\limits_{i=1}^x \sum\limits_{j=1}^y d[i][j]*i+\sum\limits_{i=1}^x \sum\limits_{j=1}^y d[i][j]*i*j$

然后用四个树状数组分别维护一下就行了。

代码:

inline void add(int x,int y,int z)
{
    for (int i=x;i<=n;i+=lowbit(i))
        for (int j=y;j<=m;j+=lowbit(j))
            tree1[i][j]+=z,tree2[i][j]+=z*x,tree3[i][j]+=z*y,tree4[i][j]+=z*x*y;//这里我不太懂为什么乘的是x和y……明白了会补上
}
inline int sum(int x,int y)
{
    long long ans=0;
    for (int i=x;i>0;i-=lowbit(i))
        for (int j=y;j>0;j-=lowbit(j))
            ans+=tree1[i][j]*(x+1)*(y+1)-tree2[i][j]*(y+1)-tree3[i][j]*(x+1)+tree4[i][j];
    return ans;
}

 

posted @ 2020-08-03 14:32  我亦如此向往  阅读(144)  评论(0编辑  收藏  举报