BinaryIndexTree(一):单点更新,区间查询

背景:

树状数组(Binary Index Tree)是一种处理区间问题较为容易实现的数据结构,可以处理单点更新,区间更新,单点查询,区间查询等问题,是一种处理在线问题的利器,其查询跟修改的时间复杂度均为O(logn)。

引入:

在算法竞赛中经常会遇到这样一类问题:给定一个数列,让你求出这个序列的某个数及其之前的数的和。通常最暴力的做法,求x位置及其之前的数的和,就是从第一个数开始依次扫到x位置,这样对于一次的查询当然没什么大问题,那么对于多次的查询,复杂度就是O(query * position),我们知道1s能跑1e9左右的数据,所以对于多次查询会TLE。那么,优化一下,我们可以通过预处理前缀和来解决这个问题,也就是设置一个pre数组,数组的下标就是代表这个位置以及之前位置的数字的和,这样我们可以在O(n)时间内预处理出数列的每一段的和,可以在O(n)时间内查询出这个第一个数到该位置的数的和,当然也可以查询一段区间的和,比如查询区间[x , y]的和,我们知道,pre[x]代表的就是[1 , x]的和,pre[y]代表的就是[1 , y]的数列和,那么我们用[1 , y]这段数列的和减去[1 , x - 1]这段数列的和,那么就是[x , y]这段数列的和了。这是一个好的trick对于没有修改操作的问题来说。

那么,如果问题变成要对某个位置的值进行修改,要是还用预处理的方法,显然,每次处理的时间为O(n - x),那么如果处理的次数比较多,显然还是并不好的,这时候,我们就需要一个有力的数据结构来维护我们的数列了。所以先来看看树状数组长啥样:

正题:

BIT就是一种简单易用的利器,在讲BIT之前,先介绍BIT的重要的一个位运算,就是lowbit(x)运算,lowbit(x) = x & (-x)

在计算机组成原理中,整数在计算机的存储一般都是补码的形式,也就是说,把一个整数x变成补码,(-x),就是把x的二进制形式每一位都取反(就是0变成1,1变成0),也就是相当于对x的二进制的最右边的1的左边的所有位取反,对于x =  来说, 他的最右边的1为从右往左第二个数,所以,-x = 。然后在讲一下,一些位运算:

按位与(&):两位都为1才是1,否则就是0

按位或(|):其中一个为1就是1,否则为0

按位异或(^):任何与0异或都是本身,1^1 = 0

所以对上面的x进行lowbit运算结果为:

x         1001011101  100

-x        0110100010  100

x & (-x)    0000000000  100

然后我们再来讲一下BIT,对于原来的数列我们记为A[i] , BIT存储的数列我们记为BIT[i],那么BIT就是一个类似前缀和的东西,而不是一个跟线段树一样的二叉树(关于线段树,之后会更),每个BIT[i]记录的是一个块的数据,先画个图see see:

图1(接下来的数组块依次类推啊,人懒懒得画):

那么我们可以看出来,每个BIT[i]覆盖的范围,就是lowbit(i),也可以理解成占领管理的范围,大概更加形象。从图1我们可以看出来,每一个块覆盖的范围就是这个lowbit(i)的长度,可以看出来其管理的地界:

B[1] = A[1]......................................................................................................................lowbit(1) = 1

B[2] = A[1] + A[2] ...........................................................................................................lowbit(2) = 2

B[3] = A[3] .....................................................................................................................lowbit(3) = 1

B[4] = A[1] + A[2] + A[3] + A[4]......................................................................................lowbit(4) = 4

......................依次类推,懒得写了...........(注意啊,数组的下标一定是从1开始的呀,不然会GG)

所以,我们就可以对BIT进行修改查询操作啦,首先介绍的是:

单点修改,区间查询:

修改很简单,就是把管辖这一位置的所有块都进行更新,比如更新位置x,那么就更新管理他的所有块,也就是从x块开始更新到n,每次间隔为lowbit(x -> n - 1),每次更新都跟走楼梯一样,看上面的图就知道啦,看看代码:

void Update(int pos , int value)
{

    while(pos <= n)
    {
        tree[pos] = tree[pos] + value;
        pos += lowbit(pos);
    }
}

每次都更新能够管到这个位置的区块,然后我们再来看看求[1 , x]的区间和?也就是从x块开始记录和,每次x往左走的步数就是lowbit(x -> 1),因为加过的区块也不会再加,所以这相当于一个下楼梯的过程,也来看一下代码:

int Query(int pos)
{
    int sum = 0;
    while(pos > 0)
    {
        sum += tree[pos];
        x -= lowbit(pos);
    }
    return sum;
}

因为BIT存储的是前缀和的形式,所以,求[x , y]的区间和也就是Query(y) - Query(x)。

时间: 2024-10-29 19:09:53

BinaryIndexTree(一):单点更新,区间查询的相关文章

Wikioi 2492 树状数组+并查集(单点更新区间查询)

刚开始做的时候用线段树做的,然后就跳进坑里了--因为要开方,所以区间的值都得全部变,然后想用lazy标记的,但是发现用不了,单点更新这个用不了,然后就不用了,就T了.然后实在不行了,看了别人的题解,原来是用树状数组+并查集的方法,唉--没想到啊! 因为开方之后多次那个数就会变成1了,所以是1的时候开方下去就没用了.树状数组更新的时候就把其更新的差更新即可,太机智了这题-- 昨天做了,然后出错找了好久都找不出来,原来是把s[i]写成c[i]了,然后答案一直错,晕-- #include <iostr

POJ 3264-Balanced Lineup(线段树:单点更新+区间查询)

Balanced Lineup Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 34522   Accepted: 16224 Case Time Limit: 2000MS Description For the daily milking, Farmer John's N cows (1 ≤ N ≤ 50,000) always line up in the same order. One day Farmer Joh

HDU 3874 Necklace (线段树单点更新+区间查询+离线操作)

Problem Description Mery has a beautiful necklace. The necklace is made up of N magic balls. Each ball has a beautiful value. The balls with the same beautiful value look the same, so if two or more balls have the same beautiful value, we just count

HDU 4027 Can you answer these queries?(线段树的单点更新+区间查询)

题目链接 题意 : 给你N个数,进行M次操作,0操作是将区间内的每一个数变成自己的平方根(整数),1操作是求区间和. 思路 :单点更新,区间查询,就是要注意在更新的时候要优化,要不然会超时,因为所有的数开几次方之后都会变成1,所以到了1不用没完没了的更新. 1 //HDU 4027 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <iostream> 6 #defi

hihoCoder week19 RMQ问题再临-线段树 单点更新 区间查询

单点更新 区间查询 #include <bits/stdc++.h> using namespace std; #define m ((l+r)/2) #define ls (rt<<1) #define rs (rt<<1|1) const int N = 2e6+10; int s[N], tr[N]; void build(int rt,int l,int r) { if(l == r) { tr[rt] = s[l]; return ; } build(ls,

TOJ 2725 See you~(二维树状数组单点更新区间查询)

描述 Now I am leaving hust acm. In the past two and half years, I learned so many knowledge about Algorithm and Programming, and I met so many good friends. I want to say sorry to Mr, Yin, I must leave now ~~>.<~~. I am very sorry, we could not advanc

HDU1166(线段树单点更新区间查询)

敌兵布阵 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 62483    Accepted Submission(s): 26386 Problem Description C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了.A国在海岸线沿直线布置了N个工兵营地,Derek和Tidy的任务就

POJ 3264 Balanced Lineup (线段树单点更新 区间查询)

Balanced Lineup Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 36820   Accepted: 17244 Case Time Limit: 2000MS Description For the daily milking, Farmer John's N cows (1 ≤ N ≤ 50,000) always line up in the same order. One day Farmer Joh

CDOJ 1073 线段树 单点更新+区间查询 水题

H - 秋实大哥与线段树 Time Limit:1000MS     Memory Limit:65535KB     64bit IO Format:%lld & %llu Submit Status Practice UESTC 1073 Appoint description:  System Crawler  (2016-04-24) Description “学习本无底,前进莫徬徨.” 秋实大哥对一旁玩手机的学弟说道. 秋实大哥是一个爱学习的人,今天他刚刚学习了线段树这个数据结构. 为