首页 > 其他分享 >谈谈树状数组

谈谈树状数组

时间:2023-12-15 17:33:45浏览次数:34  
标签:树状 int lowbit fenw 谈谈 数组 区间

fenwick tree

树状数组已经是时代的眼泪了
感觉随着各种版本的线段树出世, 连区间和时间上都跟树状数组差不多了, 而且就我个人而言, 线段树比树状数组更容易理解一些
但是毕竟树状数组码量要小, 简单也是优势

复杂度

可差分信息, 比如区间和, 是可以logn维护的, 哪怕是区间加和, 也可以用两个数组来维护
而不可差分信息, 比如区间max值, 只能(logn)^2来维护, 所幸(logn)^2并不大

不同的写法

有太多种不同的写法了
很多人写fenwick tree是拿lowbit写, 下标从1开始
然而我抄来的板子是从0开始的, 我也比较喜欢从0开始的数组

template <typename T>
class fenwick
{
public:
    vector<T> fenw;
    int n;
    fenwick(int _n) : n(_n)
    {
        fenw.resize(n);
    }
    void modify(int x, T v)
    {
        while (x < n)
        {
            fenw[x] += v;
            x |= (x + 1);
        }
    }
    void get(int x)
    {
        T v{};
        while (x >= 0)
        {
            v += fenw[x];
            x = (x & (x + 1)) - 1;
        }
        return v;
    }
};

如何理解树状数组

lowbit当然好理解, 每个vec[i]维护的是从i开始往前的lowbit位的数据
每次add的时候, 需要通过+lowbit找到自己的所有父(这里用类似线段树的方式表达了一下同样需要保存这份数据的区间)加上数据
而query(x)的时候, vec[x]保存了lowbit(x)个元素, x-lowbit指的是剩余需要求的元素数量, 来到vec[x-lowbit]继续求

x|(x+1)其实是什么呢?
放几张图
第一张是lowbit函数作用在[1, 1025)上的函数图像
image
第二张是x|(x+1)作用在[0, 2048)上的函数图像
image

能看出什么规律来吗?
有点类似是吧
然后我要放一张(x|(x+1))-x作用在[0, 2048) 的图像
image

do you get it?
如果你把他们的值打印出来的话
你会发现, 他们在区间上每一位所管辖的位数就是一样的.
为什么呢? 还记得lowbit是如何跳区间的吗?

void add(int x, int z)
{
    while (x <= n)
    {
        c[x] += z;
        x += lowbit(x);
    }
}

而第二种方法是如何跳区间的?

void modify(int x, T v)
{
    while (x < n)
    {
        fenw[x] += v;
        x |= (x + 1);
    }
}

一切尽在不言中, 如果再说仔细一些的话, 这之间的区别只是位运算的一些技巧区别

arr1 = np.arange(0, 1000)
arr2 = np.arange(1, 1001)
def op4(x):
    return (x|(x+1))+1
np.vectorize(op4)(arr1)
def op5(x):
    return x+(x&(-x))
np.vectorize(op5)(arr2)

这俩矩阵的输出是一模一样的
另一个当然也是
image

用法

  • log(n)
    在需要区间加和的时候, 一般是使用差分数组的思维, 只修改前后两个值, 这是2log(n)的复杂度
    那查询呢?
    image
    维护两个数组即可

  • (log(n))^2
    计算不可差分值的时候, 也就是(x&(x+1))-1跳到l左边的话, 就别跳了, 改成x-1, 这个区间合并的过程是logn的, 所以整体复杂度是(logn)^2

标签:树状,int,lowbit,fenw,谈谈,数组,区间
From: https://www.cnblogs.com/ryougi/p/17903843.html

相关文章

  • JS中两个数组取最大值
    如果你有两个数组,并且想要找到它们中的最大值,你可以使用Math.max()方法结合展开运算符...来实现。以下是示例代码:constarray1=[5,8,2,10];constarray2=[3,6,4,9];//使用展开运算符将两个数组合并为一个新数组constcombinedArray=[...array1,...array2];......
  • shell补-shell数组
    shell补-shell数组回顾变量的赋值方法直接赋值:a=1引用命令结果:ip=$(hostname-I|awk'{print$1}')通过read交互示参数传递:脚本/函数参数传参不了解数组之前可以用whilereadline这类方法语法:数组名称[下标],从0开始####赋值比较繁琐[root@localho......
  • 学C笔记归纳 第十四篇——一维数组
    1.什么是数组?        数组是一组相同类型元素的集合。2.数组的创建方式        type_tarr_name[const_n]        type_t            数组的元素类型    arr_name     数组名        const_n   ......
  • 微信小程序对象数组赋值的坑
    前因在小程序中使用下这种方式赋值,也就是直接修改数组对象,然后进行整个数组的setData,有时会造成一些极其离谱的问题this.data.breakdowns[e.currentTarget.dataset.index].breakdownDescription=e.detail.value;this.setData({breakdowns:this.data.breakdowns......
  • Unity shader 里面使用数组
    很多人不知道Unityshader是支持通过C#脚本,往shader脚本里写入数组的。我不知道Properties里面怎么写,但是可以用C#代码往里写。数组的总长度似乎最大2048。注意,是所有数组的总长度加一起不能超过2048。比如你写了五个数组,每个数组的长度是100,五个数组的总长度就是500。不是......
  • 函数实现一维数组基本操作
    论如何用一个代码实现一堆数字的排序,删除,插入,查找。这当然少不了我们在数组上的操作,将这些看成一个个小功能,接下来我们为了使结构直观,这里我用函数来实现这些功能首先是声明//功能voidFunction();//排序voidSort(inti,intnum);//查找voidFind(intz);//插入voidIn......
  • 代码随想录算法训练营第二天| LeetCode977.有序数组的平方、209.长度最小的子数组、59
    LeetCode977.有序数组的平方●今日学习的文章链接和视频链接代码随想录(programmercarl.com) 题目链接977.有序数组的平方-力扣(LeetCode) ●自己看到题目的第一想法昨天正好做了这道题目,总体来说就是用双指针法,要么从绝对值最小的数开始排序,要么从绝对值最大的数开......
  • 代码随想录算法训练营第二天|977.有序数组的平方、209.长度最小的子数组、59.螺旋矩阵
    LeetCode977有序数组的平方题目链接:977.有序数组的平方思路:双指针,由两侧向中间逼近 LeetCode 209.长度最小的子数组题目链接:209.长度最小的子数组思路:滑动窗口,关键点滑动窗口起始点和终止点位置关系的确定 LeetCode 59.螺旋矩阵题目链接:59.螺旋矩阵关键点:循环处理......
  • java基础语法之一维数组的应用案例
    一:概述在前面已经介绍了一维数组的相关语法知识,下面来讲一下具体案例的实现。二:具体说明<1>数组的遍历数组遍历指的是:获取数组中的每一个元素,我们可以把获取到的元素输出在控制台具体代码和运行截图如下:publicstaticvoidmain(String[]args){//定义数组并初始化......
  • 对象的数据处理方法,要对对象属性进行数组操作(list数组中每一项与column数组中的valu
       //需要对对象属性进行数组操作时,使用Object.entries()方法    varlist=['V11046_052','V11046_051','V11046_50','V11046_0511'];    varcolumn=[{'观测时间':'D_DATETIME'},{'小时内极大风速出现时间':'V......