树状数组

树状数组

用途:

① 、 修 某 个 点 的 值 — — 单 点 修 改 ①、修某个点的值——单点修改

② 、 求 某 一 区 间 的 前 缀 和 — — 区 间 求 和 ②、求某一区间的前缀和——区间求和

时 间 复 杂 度 O ( l o g 2 n ) 。 时间复杂度O(log_2n)。 O(log2n)


示例

顾 名 思 义 : 以 树 的 形 状 存 储 。 顾名思义:以树的形状存储。

以 16 个 数 的 区 间 为 例 ( 数 组 长 度 为 17 , 下 标 从 1 开 始 ) : 以16个数的区间为例(数组长度为17,下标从1开始): 16(171)

在这里插入图片描述
① 、 单 点 修 改 : ①、单点修改:

定 义 原 数 组 A , 树 状 数 组 t r , 初 始 化 为 0 , 输 入 的 过 程 实 际 上 是 一 个 单 点 修 改 操 作 。 定义原数组A,树状数组tr,初始化为0,输入的过程实际上是一个单点修改操作。 A,tr,0

每 输 入 一 个 A [ i ] , 会 影 响 树 上 t r 下 标 为 i + l o w b i t ( i ) 的 值 , 因 此 要 更 新 t r [ i + l o w b i t ( i ) ] , 直 到 i + l o w b i t ( i ) > 元 素 个 数 n 。 每输入一个A[i],会影响树上tr下标为i+lowbit(i)的值,因此要更新tr[i+lowbit(i)],直到i+lowbit(i)>元素个数n。 A[i],tri+lowbit(i),tr[i+lowbit(i)]i+lowbit(i)>n

其 中 l o w b i t ( x ) 指 x 二 进 制 形 式 下 最 低 位 的 1 代 表 的 二 进 制 数 的 值 。 其中lowbit(x)指x二进制形式下最低位的1代表的二进制数的值。 lowbit(x)x1

例 如 上 图 中 , 在 输 入 A [ 6 ] 的 值 后 , 受 影 响 的 点 是 8 和 16 , 故 要 给 节 点 8 和 16 加 上 A [ 6 ] 。 l o w b i t ( 6 ) = l o w b i t ( 110 ) = 10 = 2 , t r [ 6 + 2 ] = t r [ 8 ] + = A [ 6 ] , t r [ 8 + 8 ] = t r [ 16 ] + = A [ 6 ] 。 例如上图中,在输入A[6]的值后,受影响的点是8和16,故要给节点8和16加上A[6]。\\lowbit(6)=lowbit(110)=10=2,tr[6+2]=tr[8]+=A[6],tr[8+8]=tr[16]+=A[6]。 A[6]816816A[6]lowbit(6)=lowbit(110)=10=2,tr[6+2]=tr[8]+=A[6],tr[8+8]=tr[16]+=A[6]

显 然 地 , 修 改 的 时 间 复 杂 度 为 O ( l o g 2 n ) 显然地,修改的时间复杂度为O(log_2n) O(log2n)

② 、 区 间 求 和 ②、区间求和

树 上 节 点 t r [ i ] = A [ i ] + ∑ A [ i − l o w b i t ( i ) ] , i + l o w b i t ( i ) > 0 , 树上节点tr[i]=A[i]+ \sum A[i-lowbit(i)], i+lowbit(i)>0, tr[i]=A[i]+A[ilowbit(i)],i+lowbit(i)>0

求 区 间 [ l , r ] 的 和 : t r [ r ] − t r [ l − 1 ] 。 求区间[l,r]的和:tr[r]-tr[l-1]。 [l,r]:tr[r]tr[l1]

求 t r [ i ] 的 操 作 是 O ( l o g 2 n ) 的 , 因 此 区 间 求 和 的 时 间 复 杂 度 也 是 O ( l o g 2 n ) 的 。 求tr[i]的操作是O(log_2n)的,因此区间求和的时间复杂度也是O(log_2n)的。 tr[i]O(log2n)O(log2n)


代码:
int lowbit(int x)
{
    return x&(-x);
}

void add(int w,int p)//单点修改
{
    for(int i=p;i<=n;i+=lowbit(i)) tr[i]+=w;
}

int query(int x)//区间[1,x]的和
{
    int sum=0;
    for(int i=x;i;i-=lowbit(i)) sum+=tr[i];
    return sum;
}

例题:

题目:
给定 n 个数组成的一个数列,规定有两种操作,一是修改某个元素,二是求子数列 [a,b] 的连续和。

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

第二行包含 n 个整数,表示完整数列。

接下来 m 行,每行包含三个整数 k,a,b (k=0,表示求子数列[a,b]的和;k=1,表示第 a 个数加 b)。

数列从 1 开始计数。

输出格式
输出若干行数字,表示 k=0 时,对应的子数列 [a,b] 的连续和。

数据范围
1≤n≤100000,
1≤m≤100000,
1≤a≤b≤n
输入样例:
10 5
1 2 3 4 5 6 7 8 9 10
1 1 5
0 1 3
0 4 8
1 7 5
0 4 8
输出样例:
11
30
35

题意:

输 入 数 组 长 度 n , 操 作 数 m , 长 度 为 n 的 数 组 , m 个 操 作 。 k = 0 时 输 出 区 间 [ a , b ] 的 和 , k = 1 时 把 节 点 a 的 值 加 上 b 。 输入数组长度n,操作数m,长度为n的数组,m个操作。\\k=0时输出区间[a,b]的和,k=1时把节点a的值加上b。 n,m,nmk=0[a,b]k=1ab

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define inf 0x7fffffff
using namespace std;
const int N=1e5+10;
int n,m;
int tr[N],a[N];

int lowbit(int x)
{
    return x&(-x);
}

void add(int w,int p)
{
    for(int i=p;i<=n;i+=lowbit(i)) tr[i]+=w;
}

int query(int x)
{
    int sum=0;
    for(int i=x;i;i-=lowbit(i)) sum+=tr[i];
    return sum;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        add(a[i],i);///建树,更新树上节点的值
    }

    while(m--)
    {
        int k,a,b;
        scanf("%d%d%d",&k,&a,&b);
        if(!k) printf("%d\n",query(b)-query(a-1));
        else add(b,a);///在位置a加上b,修改
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
扁平数组转换成树状数组(也称为二维数组或矩阵)多层表示的过程涉及将一维数据按照层次结构重新组织。通常,这种转换用于解决一些特定的问题,如矩阵操作、多维索引查询等。 假设我们有一个扁平的一维数组 `flatArray`,它的元素按列顺序排列,例如: ``` flatArray = [0, 1, 2, 3, 4, 5] ``` 如果我们想要将其转换为三层的树状数组,我们可以按照以下规则构建: 第 1 层是一个包含所有元素的第一级节点,即每一项都是单个节点。 第 2 层是对第 1 层的元素分组,每个子组包含一定数量的节点,这个数量取决于你想如何划分第二层。例如,如果每行有2个元素,那么第一层的两个元素会组合在一起形成第二个层级的单一节点,而第三个元素单独形成另一个二级节点。 第 3 层可以看作是第二层的进一步细化,对于每一组的内部结构,你可以继续应用上述规则。 这里是一个简单的示例,假设有三层: ```python # 第一层 layer_1 = [0, 1, 2, 3, 4, 5] # 第二层 # 我们将元素分为两列并创建节点对 layer_2 = [(layer_1, layer_1), (layer_1, layer_1), (layer_1, layer_1)] # 第三层 # 对于每一组,我们可以创建更多的内部结构。这取决于具体的需求。这里是简单的展开示例: layer_3 = [] for pair in layer_2: # 这里可以根据需要创建更细的结构。例如,每个pair可以代表一个“小块”,该“小块”由内部元素构成。这个例子中我们简单地保持每个pair作为一个独立的小块: layer_3.extend(pair) # 输出结果: print("Layer 1:", layer_1) print("Layer 2:", layer_2) print("Layer 3:", layer_3) ``` 运行上面的代码会得到输出类似于这样的结果: ``` Layer 1: [0, 1, 2, 3, 4, 5] Layer 2: [(0, 1), (2, 3), (4, 5)] Layer 3: [0, 1, 2, 3, 4, 5] ``` 请注意,这个示例相对简陋,并未深入处理第三层的实际数据结构。实际上,在第三层,你需要根据具体的业务需求来设计数据的存储和访问方式。 **相关问题**: 1. 在什么场景下需要将扁平数组转换为树状数组或多层表示? 2. 转换过程中需要注意哪些细节? 3. 树状数组转换后如何优化查找效率?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值