初学树状数组

原理:

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

思想:

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

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

posted @ 2019-07-30 23:18  小张人  阅读(174)  评论(0编辑  收藏  举报
分享到: