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

树状数组

时间:2023-10-25 16:37:02浏览次数:31  
标签:单点 树状 cdot sum tr 查询 修改 数组

树状数组

树状数组支持的基本操作:单点修改,前缀和查询

一维树状数组

单点修改,区间查询

对于单点修改,直接修改即可。

对于区间查询,拆成 \(sum(r)-sum(l-1)\) 来做。

区间修改,单点查询

考虑在原序列的差分序列上建立树状数组。

于是单点修改转化为前缀和查询。区间修改转化为单点修改 \(tr[l]\gets tr[l]+val\),\(tr[r+1]\gets tr[r+1]-val\)。

单点修改,单点查询

只需要将单点修改看做长度为 \(1\) 的区间修改即可。

区间修改,区间查询

最毒瘤的一集,建议用线段树做

我们依然要考虑序列 \(a\) 的差分数组 \(b\)。那么我们可以将区间查询拆成两个前缀和查询,于是只需要考虑查询 \(sum(x)\)。

于是

\[\begin{aligned} sum(x)&=\sum_{i=1}^x\sum_{j=1}^i b[j]\\ &=(x+i)\sum_{i=1}^xb[i]-\sum_{i=1}^xi\cdot b[i] \end{aligned} \]

拆成两个树状数组维护,第一个树状数组维护前缀和 \(\sum b[i]\),第二个树状数组维护 \(\sum i\cdot b[i]\)。

我们考虑 \(a[i]\gets a[i]+v\)(\(\forall i \in [l,r]\)) 后 \(i\cdot b[i]\) 的变化。

\[a_{l-1}, a_l+v, a_{l+1}+v, \cdots, a_{r}+v,a_{r+1} \]

\[\iff b_{l-1},b_l+v,b_{l+1},\cdots,b_r,b_{r+1}-v \]

\[\iff (l-1)b_{l-1},l\cdot b_l\textcolor{cyan}{+l\cdot v},(l+1)\cdot b_{l+1},\cdots, r\cdot b_r, (r+1)\cdot b_{r+1}\textcolor{cyan}{-(r+1)\cdot v} \]

于是我们在维护第二个数组时只需要使 \(l\cdot b[l]\gets l\cdot b[l]+l\cdot v\),\((r+1)\cdot b[r+1] \gets (r+1)\cdot b[r+1]-(r+1)\cdot v\) 即可。

二维树状数组

单点修改,区间查询

对于单点修改,直接修改即可。

对于区间查询,拆成 \(sum(x_2,y_2)-sum(x_1-1,y_2)-sum(x_2,y_1-1)+sum(x_1-1,y_1-1)\) 来做。

区间修改,单点查询

考虑在原序列的差分序列上建立树状数组。

于是单点修改转化为前缀和查询。区间修改转化为单点修改 \(tr[x_1,y_1]\gets tr[x_1,y_1]+val\),\(tr[x_2+1,y_1]\gets tr[x_2+1,y_1]-val\),\(tr[x_1,y_2+1]\gets tr[x_1,y_2+1]-val\),\(tr[x_2+1,y_2+1]\gets tr[x_2+1,y_2+1]+val\)。

单点修改,单点查询

只需要将单点修改看做长度为 \(1\) 的区间修改即可。

区间修改,区间查询

最最毒瘤的一集,建议用二维线段树做

我们依然要考虑序列 \(a\) 的差分数组 \(b\)。那么我们可以将区间查询拆成四个前缀和查询,于是只需要考虑查询 \(sum(x,y)\)。

于是

\[\begin{aligned} sum(x,y)&=\sum_{i=1}^x\sum_{j=1}^{y}\sum_{k=1}^i\sum_{l=1}^{j} b[k,l]\\ &=\sum_{i=1}^x\sum_{k=1}^i\sum_{j=1}^{y}\sum_{l=1}^{j} b[k,l] \\ &=\sum_{i=1}^x\sum_{k=1}^i\left((y+1)\sum_{j=1}^{y}b[i,j]-\sum_{j=1}^y j\cdot b[i,j] \right) \end{aligned} \]

不妨设 \(c[i]=(y+1)\sum_{j=1}^{y}b[i,j]-\sum_{j=1}^y j\cdot b[i,j]\),继续推导:

\[\begin{aligned} sum(x,y)&=\sum_{i=1}^x\sum_{k=1}^i c[k]\\ &=(x+1)\sum _{i=1}^{x} c[i]-\sum_{i=1}^x i\cdot c[i] \end{aligned} \]

然后还原式子

\[sum(x,y)=(x+1)\sum_{i=1}^x \left((y+1)\sum_{j=1}^{y}b[i,j]-\sum_{j=1}^y j\cdot b[i,j] \right)-\sum _{i=1}^{x} i\cdot \left((y+1)\sum_{j=1}^{y}b[i,j]-\sum_{j=1}^y j\cdot b[i,j] \right) \]

\[=(x+1)(y+1)\sum_{i=1}^x\sum_{j=1}^y b[i,j]-(x+1)\sum_{i=1}^{x}\sum_{j=1}^{y} j\cdot b[i,j]-(y+1)\sum_{i=1}^{x}\sum_{j=1}^{y} i\cdot b[i,j]+\sum_{i=1}^{x}\sum_{j=1}^{y} ij\cdot b[i,j] \]

拆成四个树状数组维护即可。

标签:单点,树状,cdot,sum,tr,查询,修改,数组
From: https://www.cnblogs.com/Starrykiller/p/fenwick-tree.html

相关文章

  • JS树形数组扁平化
    如题,有时候需要对树形数组深层去找符合字段的那一串json,苦于循环找太费劲,索引选择扁平化,找起来方便很多lettreeList=[{id:'1',name:'水果',value:3,children:[{id:'1-1',......
  • 合并两个有序数组(JAVA)
    题外话在我个人的思路视角里,遇到这种排序问题总是会在脑子里产生一些画面感。让我将这些问题奔着一种奇妙的思路而去,也就是在我脑子里很简答,但难以在代码上复现,我觉得从本我的角度讲我也许天生不适合当一个高级程序员hhhh,但!我命由我不由天!题解题目给你两个按非递减顺序排列......
  • java学习-二维数组&面向对象
    动态初始化格式数据类型[][]变量名=new数据类型[m][n]m表示这个二位数组可以存放多少个以为数组n表示里面的每个一维数组可以存放多少个元素比如int[][]arr=new[3][2]这个就代表里面有3个一维数组,每个一维数组可以存放2个元素存数据arr[0][0]=11arr[0][1]=......
  • C++数组
    c++数组目录c++数组一维数组声明和初始化访问数组中元素修改数组数据遍历数组多维数组定义和初始化嵌套循环遍历指针数组动态数组参考资料数组是用来存储相同类型的变量的顺序集合。所有的数组都是由连续的内存位置组成。最低的地址对应第一个元素,最高的地址对应最后一个元素。......
  • 2023-10-24 react+ts 遍历双重对象嵌套数组
    useEffect(()=>{if(value){constarr=value;for(constkinarr){console.log(k,arr[k]);arr[k].key=arr[k].id;arr[k].title=arr[k].name;for(constk2inarr[k].children){arr[k2]......
  • 数组的拷贝与扩容
    数组的拷贝与扩容拷贝首先需要两个数组需要知道从哪里拷贝到哪里,拷贝多长//需求:从源数组里面拷贝第二个数组-第五个数组,到目标数组里面publicclassArrayCopyDemo{publicstaticvoidmain(String[]args){//源数组int[]ages={14,25,36,1......
  • C++前缀和算法应用:和至少为 K 的最短子数组的原理、源码及测试用例
    题目给你一个整数数组nums和一个整数k,找出nums中和至少为k的最短非空子数组,并返回该子数组的长度。如果不存在这样的子数组,返回-1。子数组是数组中连续的一部分。示例1:输入:nums=[1],k=1输出:1示例2:输入:nums=[1,2],k=4输出:-1示例3:输入:nums=......
  • C++算法前缀和的应用:分割数组的最大值的原理、源码及测试用例
    分割数组的最大值二分过些天整理基础知识题目给定一个非负整数数组nums和一个整数m,你需要将这个数组分成m个非空的连续子数组。设计一个算法使得这m个子数组各自和的最大值最小。示例1:输入:nums=[7,2,5,10,8],m=2输出:18解释:一共有四种方法将nums分割为2个......
  • C99中的柔性数组和其内在本质
    示例:#include<stdio.h>#include<stdlib.h>//定义一个包含柔性数组的结构体structflex_array{ intsize; intdata[0];};intmain(){ inti; intsize=10; //动态分配内存 structflex_array*arr=malloc(sizeof(structflex_array)+sizeof(int)*si......
  • JavaScript 将对象转换为数组
    JavaScript将对象转换为数组在JavaScript中,你可以使用不同的方法将对象转换为数组,具体取决于对象的结构和你希望在数组中得到什么样的数据。以下是一些常见的方法:Object.keys()方法:这种方法将对象的键转换为数组。constobj={a:1,b:2,c:3};constarr=Object......