树状数组

树状数组

关于树状数组的详细介绍可见刘汝佳《算法竞赛入门经典:训练指南》

        对于一个n元素的数组A[n],可执行如下操作:

        Add(I, d):让A[i]变成A[i]+d。

        Query(L, R):返回A[L]+A[L+1]+…+A[R]。

        注意:树状数组只能计算A[1]开始的和,A[0]这个元素是不能用的。上面操作复杂度都是O(logn)。

        其实树状数组还可以处理区间更新,单点查询的问题。如HDU 1556 Color the ball,但是此类问题还是用线段树做比较直观。

下面是代码:

const int maxn=10000+5;//最大元素个数

int n;//元素个数
int c[maxn];//c[i]==A[i]+A[i-1]+...+A[i-lowbit(i)+1]

//返回i的二进制最右边1的值
int lowbit(int i)
{
    return i&(-i);
}

//返回A[1]+...A[i]的和
int sum(int i)
{
    int res=0;
    while(i>0)
    {
        res += c[i];
        i -= lowbit(i);
    }
    return res;
}

//令A[i] += val
void add(int i,int val)
{
    while(i<=n)
    {
        c[i] += val;
        i += lowbit(i);
    }
}

注意,刚开始c数组要初始全0,然后每读入一个数A[i]就执行一步add(i, A[i])来进行真正的初始化。

且树状数组处理的数组A[n]是从小标1开始的,也即A[0]是一个没有用的元素。

 

 

树状数组:基本应用

HDU 1166 敌兵布阵(简单树状数组):基本应用。解题报告!

 

 

树状数组:标记数组,单点更新

UVA 1428 Pingpong(树状数组):基本题型,不过需要转换一下思维。解题报告!

POJ 2299Ultra-QuickSort(树状数组+离散化或归并排序求逆序):需要离散化数据然后用树状数组解决,但是用归并排序的思想更方便。解题报告!

POJ 2352HDU1541 Stars(树状数组):基本题型。解题报告!

POJ 2481 Cows(树状数组):基本题型,但是注意当两个节点完全相同时,但是我们又不能把相同的节点算入要求的节点数目里面时该怎么办?解题报告!

POJ 3067Japan(树状数组): 排序之后转化为求数列逆序数。解题报告!

HDU 1394Minimum Inversion Number(树状数组):依然是求逆序数。解题报告!

POJ 3378 CrazyThairs(数据集中+DP+树状数组+高精度):综合应用不过DP不难,数据集中不难,树状数组不难,高精度也不难。解题报告!

HDU 3450Counting Sequences(树状数组+DP+离散化):综合题,一步步拆解。解题报告!

HDU 3743 FroshWeek(树状数组或归并排序求逆序):又是一题求逆序的。解题报告!

HDU 2838 CowSorting(树状数组):很巧妙的思维。解题报告!

POJ 2182 LostCows(树状数组,暴力解法):暴力解法更好理解一点。解题报告!

POJ 1990MooFest(树状数组+离线处理): 需要一定的转换思维。解题报告!

 

 

树状数组:区间更新,单点查询

HDU 1556 Colorthe ball(树状数组):区间更新。解题报告!

 

 

树状数组:单点更新,删除元素

HDU 3874Necklace(树状数组+离线处理):需要巧妙的删除元素。解题报告!

 

### 树状数组的数据结构实现及应用 #### 什么是树状数组树状数组(Binary Indexed Tree, BIT)是一种基于数的二进制特性的高效数据结构,用于支持动态数组上的区间查询和单点/区间更新操作。它通过一种特殊的树形结构来存储累积信息,从而能够在对数时间内完成这些操作[^1]。 #### 树状数组的核心特性 树状数组的主要特点在于其编程简单性和高效的性能表现。相比于其他复杂的数据结构(如线段树),树状数组仅需少量代码即可实现核心功能,并且运行速度更快[^3]。 #### 基本操作原理 树状数组的操作依赖于 `lowbit` 技术,该技术能够提取整数最低位的1及其后续零所表示的数值。具体而言: - **Lowbit函数定义**:对于任意正整数 \( x \),\( lowbit(x) = x \& (-x) \)[^4]。 此函数帮助定位父节点以及子节点之间的关系,在构建和维护树状数组时起到关键作用。 #### 查询与更新方法 以下是树状数组两种基本操作的具体实现: ##### 单点更新 当需要改变原始数组中的某一项值时,必须同步调整树状数组对应位置及其祖先结点的信息。算法如下所示: ```cpp void update(int idx, int delta){ while(idx <= n){ t[idx] += delta; idx += lowbit(idx); } } ``` ##### 区间求和 为了计算从索引1到指定索引处所有元素之和,可以采用累加方式逐步访问相关联的节点直到根部为止。伪代码形式呈现如下: ```cpp int query(int idx){ int res = 0; while(idx > 0){ res += t[idx]; idx -= lowbit(idx); } return res; } ``` 上述两部分构成了完整的树状数组框架,适用于处理频繁发生的范围汇总请求与局部修正指令组合场景下的优化解决方案[^2]^。 #### 应用实例分析 假设存在一个长度为n的一维数组a[],目标是对这个序列执行多次交替进行的修改命令(增加或者减少特定下标的数值大小)同时间歇性询问关于不同片段内的总合状况,则运用BIT将是理想的选择之一因为每次交互都能维持O(logN)的时间消耗级别. ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值