首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

  • 24-12-06 00:05
  • 3787
  • 8692
juejin.cn

说起树状数组,那么首先要掌握差分和前缀和这两个概念(大部分题就相关)

前言

差分和前缀和的概念是相反的,原数组是差分数组的前缀和

也就是原数组a[],差分数组d[],满足d[i]=a[i]-a[i-1];

a[i]=d[1]+....+d[i]

前缀和相反

树状数组

树状数组我感觉就是对差分数组的一种二进制压缩,原本来说差分数组查询原数组的时间复杂度为n,经过差分数组的压缩变为log2n

他是利用二叉树的样式来存储

切入点:lowbit函数

由于电脑一种叫做补码的操作(由于电脑是二进制,它们存的相反数是它的取反+1),一个数与它的相反数做与操作时会返回二进制下最右边的1的位置。举个例子: 6&-6=2 将6变成二进制:110。其中最右侧的1加粗字体:110则返回的是二进制下10的值:2。得到代码:

arduino
代码解读
复制代码
public static int lowbit(int x){ return x&-x; }

利用这个性质建立树状数组:

为了简化区间修改的效率,我们需要建立这样的一个数组:数组中第k位的值为原数组中的一段区间和,这个区间的长度是lowbit(k),终点是k。 比如:输入一个数组,那么我们所建立的数组:

  • 第一位(1在二进制下=1 二进制下的1=1)的值为输入的数组的第一位往前的一位的和,也就是第一位。
  • 第二位(2在二进制下=10 二进制下的10=2)的值为输入的数组的第二位往前两位的和,第一位和第二位。
  • 第三位(3在二进制下=11 二进制下的1=1)的值为输入的数组的第三位往前一位的和,也就是第三位。
  • 第四位的值(4在二进制下=100 二进制下的100=4)的值为输入的数组的第四位往前四位的和,也就是第一位,第二位,第三位以及第四位。

我们就称这种数组为树状数组

scss
代码解读
复制代码
static void update(int x,int val){ //x:修改的位置 val: 增加的值 while (x<=N){ tree[x]+=val; x+=lowbit(x); } }

这样就很好传递了,有点动归的意思哈

相当于7二进制位111,利用lowbit查询到最后一个1的位置为1减去为110(6),重复得到100(4),重复得到000(舍去)这样sum[7]=tree[6]+tree[4]+tree[7],这样修改后的原数组查询就大大减少时间复杂度了

ini
代码解读
复制代码
static int sum(int x){ int ans=0; while (x!=0){ ans+=tree[x]; x-=lowbit(x); } return ans; }

修改区间:

根据题意去设定tree存储的是前缀和还是差分

1.单点修改+区间和查询使用前缀和的思想

ini
代码解读
复制代码
for (int i = 1; i <= n; i++){ int x=sc.nextInt(); //初始化数据 update(i,x); } for (int i = 1; i <= m; i++){ int op=sc.nextInt(); if(op==1){ //修改第a,+k int a=sc.nextInt(); int k=sc.nextInt(); update(a,k); } if(op==2){ //查询区间{a,b} int a=sc.nextInt(); int b=sc.nextInt(); System.out.println(sum(b)-sum(a-1)); }

2.区间修改+单点查询使用差分的思想

ini
代码解读
复制代码
for (int i = 1; i <= n; i++){ int x=sc.nextInt(); //初始化数据 update(i,x); update(i+1,-x); } for (int i = 1; i <= m; i++){ int op=sc.nextInt(); if(op==1){ //修改[a,b]区间内+k int a=sc.nextInt(); int b=sc.nextInt(); int k=sc.nextInt(); update(a,k); update(b+1,-k); } if(op==2){ //查询 int a=sc.nextInt(); System.out.println((long) sum(a)); } }

这两个的区别就在于初始化tree数组的方法

3.区间修改+区间查询

可以使用线段树的方式去解答

这里采用差分的思想去解答(因为差分可以对原数组进行修改查询)

但是区间和和差分相差两段累加和的结果.但是我们不难发现,区间和和差分数组还是有关系的

tree[1]+tree[2]+……tree[i]=a[i]

那么对于区间[l,r]

a[1]+a[2]+……+a[r-1]+a[r] //用上方公式推导得出

=tree[1]+(tree[1]+tree[2])+……+(tree[1]+……+tree[r]) //根据加法交换律与结合律:

=(tree[1](r))+(tree[2](r-1))+……(tree[r]*1) //那么:

=r*(tree[1]+tree[2]+……+tree[r])-(tree[1]*0+tree[2]1+……+tree[r](r-1))

这样我们一样可以通过sum来得到区间和

单独用tree1[i]=tree[i]*(i-1)存储,然后计算tree1的前缀和就行了

代码:

ini
代码解读
复制代码
for (int i = 1; i <= n; i++) { long x=sc.nextInt(); update(i,x-old,tree1); update(i,(i-1)*(x-old),tree2); old=x; } while (m-->0){ int op=sc.nextInt(); if(op==1){ long l=sc.nextInt(); long r=sc.nextInt(); long k=sc.nextInt(); update(l,k,tree1); update(r+1,-k,tree1); update(l,(l-1)*k,tree2); update(r+1,-r*k,tree2); }else { long l=sc.nextInt(); long r=sc.nextInt(); System.out.println(r*sum(r,tree1)-sum(r,tree2)-(l-1)*sum(l-1,tree1)+sum(l-1,tree2)); } }
注:本文转载自juejin.cn的不可能ac的文章"https://juejin.cn/post/7444475321929334824"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

143
阅读
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top