zoukankan      html  css  js  c++  java
  • 初学树状数组

    原理:

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

    思想:

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

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

  • 相关阅读:
    定时器
    Vue CLI环境变量
    负数的二进制表示方法
    IDEA指定启动JDK版本
    Windows7安装两个jdk配置
    Bloom Filter 数据结构去重
    新浪微博爬虫参考
    Spring Data JPA
    Spring的JDBC框架
    数据库连接池:Druid
  • 原文地址:https://www.cnblogs.com/zhanhonhao/p/11273024.html
Copyright © 2011-2022 走看看