• 初学树状数组


    原理:

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

    思想:

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

    首先,树状数组之所以叫树状数组,因为它像树一样,有类似树的父子节点关系,这点在更新和求和操作上体现的最为明显。而最终也只是数组,因为实现起来简单方便,如数组一样。(一开始还纳闷为什么不叫二进制索引树),英文名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

  • 相关阅读:
    SharePoint每日小贴士Web部件
    韦东山设备树课程-环境搭建【学习笔记】
    韦东山视频第3课第2节_JNI_C调用JAVA_P【学习笔记】
    韦东山视频第3课第1节_JNI_P【学习笔记】
    高通qxdm抓取sensor的log【学习笔记】
    sensor【学习笔记】
    linux驱动由浅入深系列:高通sensor架构实例分析之二(驱动代码结构)【转】
    linux驱动由浅入深系列:高通sensor架构实例分析之三(adsp上报数据详解、校准流程详解)【转】
    Android Sensor 架构深入剖析【转】
    Android Sensor详解(1)简介与架构【转】
  • 原文地址:https://www.cnblogs.com/zhanhonhao/p/11273024.html
Copyright © 2020-2023  润新知