树状数组的基础拓展

                                          蒟蒻成长日记  7.24 树状数组的基础拓展
                                                               树状数组
                                                                             ———————短小精悍的手术刀

可以解决的基本问题:
1.单点更新和单点查询操作
2.单点更新和区间查询操作
3.区间更新和单点查询操作
4.区间查询和区间更新操作
上一篇博客简单的介绍了树状数组的初步露面,代码上对应的也比较简单,只介绍了树状数组的裸模板,下面通过题目简单讨论一下树状数组的的简单应用也就是支持的3,4操作

注:裸树状数组的基本函数有:add(int x,int c)//在x位置加上c,sum(int x)//返回[1,x]的区间和,lowbit(int x)


3.区间更新和单点查询操作

洛谷3374

题目描述
如题,已知一个数列,你需要进行下面两种操作:

将某一个数加上 x

求出某区间每一个数的和

输入格式
第一行包含两个正整数 n,m,分别表示该数列数字的个数和操作的总个数。

第二行包含 n 个用空格分隔的整数,其中第 i 个数字表示数列第 i 项的初始值。

接下来 m 行每行包含 3 个整数,表示一个操作,具体如下:

1 x k 含义:将第 x 个数加上 k

2 x y 含义:输出区间 [x,y] 内每个数的和

输入输出样例

输入样例:

5 5
1 5 4 2 3
1 2 4 2
2 3
1 1 5 -1
1 3 5 7
2 4

输出样例:

6
10



思路:首先我们裸的树状数组是支持的单点更新和区间查询操作,复杂度是log级别,本题要求我们解决的是区间查询和单点更新操作,不难想到我们在学前缀和的时候也学过一个差分来解决区间查询操作,本题加一个差分,把树状数组构造成差分数组就可以了,这也是树状数组的一个简单模板。

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int n,m;
int q[N];
int tr[N];
int lowbit(int x)
{
	return x&-x;
} 
void add(int x,int c)
{
	for(int i=x;i<=n;i+=lowbit(i))tr[i]+=c; 
}
int sum(int x)
{
	int res=0;
	for(int i=x;i;i-=lowbit(i))res+=tr[i];
	return res;
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)scanf("%d",&q[i]);
	for(int i=1;i<=n;i++)add(i,q[i]-q[i-1]);
	
	while(m--)
	{
		int a,b,c,d;
		scanf("%d",&a);
		if(a==1)
		{
			scanf("%d%d%d",&b,&c,&d);
			add(b,d),add(c+1,-d);
		}
		else 
		{
			scanf("%d",&c);
			printf("%d\n",sum(c));
		}
	}
}

4.区间查询和区间更新操作

一个简单的整数问题2:acwing 243题

题意:
给定一个长度为 NN 的数列 AA,以及 MM 条指令,每条指令可能是以下两种之一:

  1. C l r d,表示把 A[l],A[l+1],…,A[r]A[l],A[l+1],…,A[r] 都加上 dd。
  2. Q l r,表示询问数列中第 l∼rl∼r 个数的和。

对于每个询问,输出一个整数表示答案。

输入格式

第一行两个整数 N,MN,M。

第二行 NN 个整数 A[i]A[i]。

接下来 MM 行表示 MM 条指令,每条指令的格式如题目描述所示。

输出格式

对于每个询问,输出一个整数表示答案。

每个答案占一行。

 

输入样例:

10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4

输出样例:

4
55
9
15


思路:推公式加差分






有了思路,代码就是在模板上改了一下

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
typedef long long LL; 
LL tr1[N],tr2[N];
int n,m;
int q[N];
int lowbit(int x)
{
	return x&-x;
}
void add(int x,LL tr[],LL c)
{
	for(int i=x;i<=n;i+=lowbit(i))tr[i]+=c;
}
LL sum(LL x)
{
	LL res1=0,res2=0;
	for(int i=x;i;i-=lowbit(i))res1+=tr1[i],res2+=tr2[i];
	
	return x*res1-res2;
	
	
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)scanf("%d",&q[i]);
	
	for(int i=1;i<=n;i++)add(i,tr1,q[i]-q[i-1]),add(i,tr2,(LL)(i-1)*(q[i]-q[i-1]));
	char op[2];
	LL  a,b,d;
	while(m--)
	{
		scanf("%s%lld%lld",op,&a,&b);
		if(*op=='Q')
		  printf("%lld\n",sum(b)-sum(a-1));
		  
		else 
		  {
		  	scanf("%lld",&d);
		  	add(a,tr1,d),add(b+1,tr1,-d);
		  	add(a,tr2,(a-1)*d),add(b+1,tr2,b*(-d));
		  }
	}
} 



参考资料:
acwing算法提高课 树状数组
CSDN 博主Ac_dream  树状数组_AC__dream的博客-CSDN博客



 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
树状数组(Fenwick Tree)是一种用于快速维护数组前缀和的数据结构。它可以在 $O(\log n)$ 的时间内完成单点修改和前缀查询操作,比线段树更加简洁高效。 下面是 Java 实现的树状数组详解: 首先,在 Java 中我们需要使用数组来表示树状数组,如下: ``` int[] tree; ``` 接着,我们需要实现两个基本操作:单点修改和前缀查询。 单点修改的实现如下: ``` void update(int index, int value) { while (index < tree.length) { tree[index] += value; index += index & -index; } } ``` 该函数的参数 `index` 表示要修改的位置,`value` 表示修改的值。在函数内部,我们使用了一个 `while` 循环不断向上更新树状数组中相应的节点,直到到达根节点为止。具体来说,我们首先将 `tree[index]` 加上 `value`,然后将 `index` 加上其最后一位为 1 的二进制数,这样就可以更新其父节点了。例如,当 `index` 为 6 时,其二进制表示为 110,最后一位为 2^1,加上后变为 111,即 7,这样就可以更新节点 7 了。 前缀查询的实现如下: ``` int query(int index) { int sum = 0; while (index > 0) { sum += tree[index]; index -= index & -index; } return sum; } ``` 该函数的参数 `index` 表示要查询的前缀的结束位置,即查询 $[1, index]$ 的和。在函数内部,我们同样使用了一个 `while` 循环不断向前查询树状数组中相应的节点,直到到达 0 为止。具体来说,我们首先将 `sum` 加上 `tree[index]`,然后将 `index` 减去其最后一位为 1 的二进制数,这样就可以查询其前一个节点了。例如,当 `index` 为 6 时,其二进制表示为 110,最后一位为 2^1,减去后变为 100,即 4,这样就可以查询节点 4 的值了。 最后,我们还需要初始化树状数组,将其全部置为 0。初始化的实现如下: ``` void init(int[] nums) { tree = new int[nums.length + 1]; for (int i = 1; i <= nums.length; i++) { update(i, nums[i - 1]); } } ``` 该函数的参数 `nums` 表示初始数组的值。在函数内部,我们首先创建一个长度为 `nums.length + 1` 的数组 `tree`,然后逐个将 `nums` 中的元素插入到树状数组中。具体来说,我们调用 `update(i, nums[i - 1])` 来将 `nums[i - 1]` 插入到树状数组的第 `i` 个位置。 到此为止,我们就完成了树状数组的实现。可以看到,树状数组的代码比线段树要简洁很多,而且效率也更高。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值