初学树状数组

原理:

有好的博客做讲解了(见参考文章),这里暂时略过,如果以后有新的理解和体会会再来写的。(应该不会)

思想:

这里可以把树状数组的精妙之处提一下(我理解的)

首先,树状数组之所以叫树状数组,因为它像树一样,有类似树的父子节点关系,这点在更新和求和操作上体现的最为明显。而最终也只是数组,因为实现起来简单方便,如数组一样。(一开始还纳闷为什么不叫二进制索引树),英文名BIT(Binary Index Tree)。这个数据结构实现的功能像线段树一样,两者有着异曲同工之妙。

其次,树状数组的神奇之处在于他把数的二进制的关系引入到了数组里,具体的,将数组的下表关系和所包含数的内涵巧妙的建立了联系,同时也引入了树的概念,使得能够在\(O(log_2n)\)的时间内实现修改与查询操作。

然后,基于上一点,树状数组功能强大之处在于求前缀和,要想求区间和还要更新区间和,就不可避免的与差分的思想合二为一,融为一体。最后实现区间查询更是引入了两个差分数组来实现,使得复杂度维持在\(O(log_2n)\)。由此可见,时间的维持以空间作为了代价,往往还有思维上的跨越。

代码:

#include <iostream>
#define max_n 1005
using namespace std;
int a[max_n];//原数组
int c[max_n];//树状数组
int n;//元素个数
int x,y;//更新区间和查询区间
int k;//改变值
int lowbit(int i)//求二进制最后一的位所对应的值(lowbit(8(1000))= 8)
{
    return i&(-i);
}
void update(int i,int k)//原始树状数组更新操作
{
    while(i<=n)
    {
        c[i]+=k;
        i+=lowbit(i);
    }
}
long long sum(int i)//原始树状数组求和操作
{
    long long res = 0;
    while(i>0)
    {
        res += c[i];
        i-=lowbit(i);
    }
    return res;
}
void update_1(int i,int k)//引入一次差分树状数组后的更新操作
{
    while(i<=n)
    {
        c[i]+=k;
        i+=lowbit(i);
    }
}
long long sum_1(int i)//引入一次差分树状数组后的求和操作
{
    long long res = 0;
    while(i>0)
    {
        res+=c[i];
        i-=lowbit(i);
    }
    return res;
}
int sum1[max_n];//D[i]
int sum2[max_n];//D[i]*(i-1)
void update_2(int i,int k)//引入两次差分树状数组后的更新操作
{
    int x = i;
    while(i<=n)
    {
        sum1[i]+=k;
        sum2[i]+=k*(x-1);
        i+=lowbit(i);
    }
}
long long sum_2(int i)//引入两次差分树状数组后的求和操作
{
    long long res = 0;
    int x = i;
    while(i>0)
    {
        res += x*sum1[i]-sum2[i];
        i-=lowbit(i);
    }
    return res;
}
int main()
{
    //区间更新,单点查询
    /*cin >> n;
    for(int i = 1;i<=n;i++)
    {
        cin >> a[i];
        update_1(i,a[i]-a[i-1]);
    }
    cin >> x >> y >> k;//在[x,y]上增加k
    update_1(x,k);//差分数组中x位增加k
    update_1(y+1,-k);//差分数组中y+1位减少k
    cout << sum(2) << endl;//a[2]*/

    //区间更新,区间查询
    cin >> n;
    for(int i = 1;i<=n;i++)
    {
        cin >> a[i];
        update_2(i,a[i]-a[i-1]);
    }
    cin >> x >> y >> k;
    update_2(x,k);//对应位置上两个差分数组的初位置的处理
    update_2(y+1,-k);//对应位置上两个差分数组的末位置的处理
    cout << sum_2(y)-sum_2(x-1) << endl;//[x,y]的区间和
    return 0;
}

参考文章:

好的博客在这里,讲的清楚

Xenny,树状数组详解,https://www.cnblogs.com/xenny/p/9739600.html

原文地址:https://www.cnblogs.com/zhanhonhao/p/11273024.html

时间: 2024-10-11 16:13:13

初学树状数组的相关文章

HDU_2642_二维树状数组

Stars Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 32768/65536 K (Java/Others)Total Submission(s): 1628    Accepted Submission(s): 683 Problem Description Yifenfei is a romantic guy and he likes to count the stars in the sky.To make the pr

从线段树的可删减性谈树状数组

这可能是我最后一次更新博客了呢 # 前言 很久之前,我初学树状数组的时候感觉非常的复杂.神奇.晦涩难懂... 果然还是我太菜了.后来了解到线段树的可删减性,这两者就自然而然的联系在一起了. # 线段树的可删减性 很显然,对于一些区间操作的问题,线段树有着绝对的优势,基本上只要是区间查询之类的问题,那线段树是没跑了. 但是如果我们要查询的是前缀和这样的东西的话,会不会感觉线段树中的一些东西是多余的呢? 这么说可能有些不好理解,画画图就好: 上图如果看不清楚,请在新标签页中打开.(蓝色为节点标号,红

RMQ &amp;&amp; 树状数组 (初学)

先复习一下今天刚学的RMQ算法知识; RMQ算法(Range Minimum Query) :1.算法思想 求静态范围最值问题,适合于静态连续区间查询. A[ i ] [ j ] 的值代表的是原数组中以 i 开始的连续 (1<< j)个数的最值. 2.代码 <pre name="code" class="cpp">//2.1 预处理代码 for(int j = 1 ; j != 20 ; ++j ) // 代表区间大小 for(int i =

树状数组(BIT)初学

BIT(Binary Indexed Tree,BIT) 树状数组.树状数组是一类怎样的数据结构呢?我们知道,树状数组是用来解决动态连续和的查询问题而诞生的. 数据结构就是说,给你n个元素的数组a[1],a[2],a[3]....a[n](下标要从1开始)然后,支持以下两种操作: 1.Add(x,y)就是说对下标为x的a[x]加d.模板默认为update(x,y) 2.Query(L,R)就是计算a[L]+a[L+1]+...+a[R].模板默认为read(x)求出1-x的和,然后read(y)

MooFest(POJ-1990)(树状数组)

最近学习了一下树状数组,这道题纠结了很久,终究是因为没有明白树状数组怎么用. 感觉网上许多大神都只是讲原理,对于我们这些初学的菜鸟恐怕都被吓跑了. 这里我就以实用主义说一下使用方法(其实我觉得其原理应该能对我们更有启发,也许会带来很多潜在的好处): 这里需要注意的是,bit的实现代码中的bit数组一开始必须清零,这个数组并不是用来储存元素的,而是为实现这个数据结构而存在的.  你需要存储的元素是要通过那个add函数添加的,而求和则是要通过sum函数实现的,而这个bit数组的结构并不是对于一个新手

[shyのJAVA初探]hdu1166●树状数组

一开始shy是为了大数而走近java,随后情不自禁地就希望能初步了解java的语言特点. java初学对c++选手而言可谓简单非常.因为java的语法和c++的语法简直一样(虽然这话不太严谨,容易遭到很多反驳,不过,,shy实在是没有见过如此相像的两种语言).比如,①java开变量的方式是:int x;char c;boolean b;②java的for循环:for(int i=1;i<=n;i++){}③java的条件语句:if(--cas>0&&str!="end

HDU 5542 The Battle of Chibi dp+树状数组

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5542 题意:给你n个数,求其中上升子序列长度为m的个数 可以考虑用dp[i][j]表示以a[i]结尾的长度为j的上升子序列有多少 裸的dp是o(n2m) 所以需要优化 我们可以发现dp的第3维是找比它小的数,那么就可以用树状数组来找 这样就可以降低复杂度 #include<iostream> #include<cstdio> #include<cstring> #include

(POJ 3067) Japan (慢慢熟悉的树状数组)

Japan Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 29295   Accepted: 7902 Description Japan plans to welcome the ACM ICPC World Finals and a lot of roads must be built for the venue. Japan is tall island with N cities on the East coas

【二维树状数组】See you~

https://www.bnuoj.com/v3/contest_show.php?cid=9148#problem/F [题意] 给定一个矩阵,每个格子的初始值为1.现在可以对矩阵有四种操作: A x y n1 :给格点(x,y)的值加n1 D x y n1: 给格点(x,y)的值减n1,如果现在格点的值不够n1,把格点置0 M x1 y1 x2 y2:(x1,y1)移动给(x2,y2)n1个 S x1 y1 x2 y2 查询子矩阵的和 [思路] 当然是二维树状数组 但是一定要注意:lowbi