zoukankan      html  css  js  c++  java
  • 二维树状数组 学习笔记

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

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

    以下所有问题均在二维中

    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]=sumlimits_{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=sumlimits_{i=1}^x sumlimits_{j=1}^y sumlimits_{k=1}^i sumlimits_{h=1}^j d[k][h]$

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

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

    $ans=(x+1)*(y+1)sumlimits_{i=1}^x sumlimits_{j=1}^y d[i][j]-(x+1)sumlimits_{i=1}^x sumlimits_{j=1}^y d[i][j]*j-(y+1)*sumlimits_{i=1}^x sumlimits_{j=1}^y d[i][j]*i+sumlimits_{i=1}^x sumlimits_{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;
    }
  • 相关阅读:
    Bootstrap-CL:警告
    Bootstrap-CL:略缩图
    Bootstrap-CL:页面标题
    Bootstrap-CL:超大屏幕
    Bootstrap-CL:徽章
    Bootstrap-CL:标签
    Bootstrap-CL:分页
    Bootstrap-CL:面包屑导航
    Bootstrap-CL:导航栏
    Bootstrap-CL:导航元素
  • 原文地址:https://www.cnblogs.com/Invictus-Ocean/p/13426120.html
Copyright © 2011-2022 走看看