树状数组的基操

Posted jjsnow

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树状数组的基操相关的知识,希望对你有一定的参考价值。

前沿:数据结构

任何一个数据结构都逃不开这么几个东西:增,删,改,查

-------------------------------------------------------------------

树状数组,也叫做二叉索引树(BIT)。

是一个用来进行区间运算的数据结构。

在一定程度上可以代替线段树。

树状数组有以下几个特征:
1.巧妙地利用了位运算

2.巧妙地结合了树的数据结构的思想来处理区间问题

3.树状数组的本质还是用来维护序列的前缀

--------------------------------------------------------------------

树状数组有以下几个基本操作:

1.树的节点转移(位运算)//是2,3,4,的根基

2.单点修改

3.区间修改

4.区间查询

--------------------------------------------------------------------

首先树状数组的大致情况,我给出了一下资料:

算法竞赛入门经典-训练指南-第194-197页 

--------------------------------------------------------------------

接下来就是以上操作的具体实现的代码:

等等,先给出一些数组

c[]表示树状数组,a[]表示原序列

好了,let‘s go

1.树的节点转移(位运算)//关于这个函数的作用和这样做的原因,在上面提到的书中有讲到

int lowbit(int x)
{
    return x&(-x);
}

2,单点修改

与线段树相同的是,树状数组如果修改了某一个点的话,势必会影响到上层区间的维护的值,那么单点修改的关键就是怎么维护上层区间的维护的值

下面我给出一个函数:

void add(int d,int x)
{
    while(x<=n)
    {
        c[x]+=d;
        x+=lowbit(x);
    }
}

3.区间修改

这个涉及到了差分数组的运用

有关差分数组和这一部分的数学证明,我在一下链接种给出:
https://www.cnblogs.com/lcf-2000/p/5866170.html

void qujian_update(int* arr,int x,int d)
{
    while(x<=n)
    {
        arr[x]+=d;
        x+=lowbit(x);
    }
}

4.区间查询

int query(int* arr,int x)
{
    int ret=0;
    while(x>0)
    {
        ret+=arr[x];
        x-=lowbit(x);
    }
    return ret;
}

--------------------------------------------------------------------

现在,我给出一份完整的函数代码:
第一份:没有区间修改的树状数组

#include<bits/stdc++.h>
using namespace std;
int c[20];
int lowbit(int x)//作用:返回查找结点的下标
{
    return x&-x;
}
int sum(int x)//返回1~x的数组的前缀和
{
    int ret=0;
    while(x>0)
    {
        ret+=c[x];
        x-=lowbit(x);
    }
    return ret;
}
void add(int x,int d)//即能够起到修改区间的作用,又能够起到构建树状数组(二叉索引树)的作用
{
    while(x<=n)
    {
        c[x]+=d;
        x+=lowbit(x);
    }
}
int query(int l,int r)//作用:查询l~r的区间和,注意:sum[l,r]=c[r]-c[l-1];
{
    int ret=0;
    ret=sum(r)-sum(l-1);
    return ret;
}
int main()
{
    int n,a[10];
    cin>>n;
    memset(c,0,sizeof(c));
    for(int i=1;i<=n;++i)
    {
        cin>>a[i];//输入的数组
        add(i,a[i]);//不能从0开始,因为0会让lowbit进入死循环
    }
    string s;
    while(cin>>s)
    {
        if(s=="query")//查询的命令
        {
            int l,r;
            cin>>l>>r;
            cout<<query(l,r)<<endl;
        }
        else if(s=="add")//修改的命令,在a[x]上加上d => a[x]=a[x]+d
        {
            int d,x;
            cin>>d>>x;
            add(x,d);
        }
        else if(s=="change")//单点更新=>a[x]=d
        {
            int d,x;
            cin>>d>>x;
            add(x,d-a[x]);
        }
    }
    return 0;
}

第二份:带区间修改的树状数组

#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define maxn 1200000
#define mod 123456789
using namespace std;
typedef long long ll;
int c1[maxn],n,c2[maxn],a[maxn];
int lowbit(int x)
{
    return x&(-x);
}
void add(int d,int x)
{
    while(x<=n)
    {
        c[x]+=d;
        x+=lowbit(x);
    }
}
void qujian_update(int* arr,int x,int d)
{
    while(x<=n)
    {
        arr[x]+=d;
        x+=lowbit(x);
    }
}
int query(int* arr,int x)
{
    int ret=0;
    while(x>0)
    {
        ret+=arr[x];
        x-=lowbit(x);
    }
    return ret;
}
int main()
{
    cin>>n;
    mem(c1,0);
    mem(c2,0);
    mem(a,0);
    for(int i=1;i<=n;++i)
    {
        cin>>a[i];
        qujian_update(c1,i,a[i]-a[i-1]);
        qujian_update(c2,i,(i-1)*(a[i]-a[i-1]));
    }
    string s;
    while(cin>>s)
    {
        if(s=="change")
        {
            int l,r,d;
            cin>>l>>r>>d;
            qujian_update(c1,l,d);
            qujian_update(c1,r+1,-d);
            qujian_update(c2,l,d*(l-1));
            qujian_update(c2,r+1,-d*r);
        }
        else
        {
            int l,r;
            cin>>l>>r;
            int tmp1=(l-1)*query(c1,l-1)-query(c2,l-1);
            int tmp2=r*query(c1,r)-query(c2,r);
            int ans=tmp2-tmp1;
            cout<<ans<<endl;
        }
    }
    return 0;
} 

 




以上是关于树状数组的基操的主要内容,如果未能解决你的问题,请参考以下文章

树的基操

liunx系统的基操命令2

浅析树状数组

JavaScript之基操

树状数组板子

数据结构之树状数组从零认识树状数组