浅谈树状数组

Posted 天下风云出我辈

tags:

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

  树状数组是一种数据结构,支持一些区间操作,比线段树好写,同样也比线段树的功能少一些。

  先来看一张图(摘自百度百科)

                  

  树状数组就是这个样子的,但是树状数组的空间复杂度是O(n)的,它不像线段树那样每个节点的信息都上传的父亲去保存,他是由某一位来保存前一段区间的信息,比如说和。那么是哪一位又是保存多长的区间那?这就到了数状数组的主角lowbit上场了,lowbit是一个很简单又很复杂的东西,说它简单是因为写起来非常的简单,说它复杂是因为理解起来比较麻烦,lowbit(x)的功能是求出来x的二进制中最后一个1是多少,举个例子lowbit(7)=1,因为7的二进制是111,它的最后一个1是1,而lowbit(6)=2,因为6的二进制是110,它的最后一个1是2.

  有的讲树状数组的是把lowbit的原理讲了一遍的,个人感觉没用其实是窝不会,只需要记得lowbit(x)的求法是x&(-x)就好了,然后树状数组的第i位记录的就是从i-lowbit(i)+1开始到i这一段的信息,也就是从i往前lowbit(i)那么长,总之这样沿着lowbit就可以做到不重不漏了。

  树状数组最基本的操作就是点修改区间查询,点修改区间查询和区间修改点查询。

1.点修改区间查询

  点修改不只是要修改一个点,还要把记录了这个点的信息的那些点也修改了,这时还是要沿着lowbit走,当前点不断的加lowbit,每次到的点都修改一下,而区间查询就更容易了,如果查询区间是[l,r],那么久查询一下[1,r],在查询一下[1,l-1]然后相减久ok了,然后查询就是沿着lowbit减,把每一位的数加起来,好比查询[1,7],就查7,[5,6],[1,4]就可以了。

模板:https://www.luogu.org/problemnew/show/P3374

 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 const int maxn=10002;
 5 int n,m;
 6 int tree[maxn];
 7 int x,y,opt;
 8 int lowbit(int x)
 9 {
10     return x&(-x);
11 }
12 void add(int x,int y)
13 {
14     for(int i=x;i<=10000;i+=lowbit(x))
15         tree[i]+=y;
16 }
17 int query(int x)
18 {
19     int ans=0;
20     for(int i=x;i>=1;i-=lowbit(i))
21         ans+=tree[i];
22     return ans;
23 }
24 int main()
25 {
26     scanf("%d%d",&n,&m);//n个数,m个操作
27     for(int i=1;i<=n;++i)
28     {
29         scanf("%d",&x);
30         add(i,x);
31     } 
32     while(m--)
33     {
34         scanf("%d",&opt);
35         if(opt==1)
36         {
37             scanf("%d%d",&x,&y);
38             add(x,y);
39         }
40         else 
41         {
42             scanf("%d%d",&x,&y);
43             printf("%d\\n",query(y)-query(x-1)); 
44         } 
45     }
46     return 0;
47 } 
View Code

2.区间修改点查询

   用一个差分的思想转变成点修改区间查询。也就是把每一个数和前面的数做一下差,然后把这个新的数组放入树状数组中,这样区间修改就是在l那里加一下 ,在r+1那里减一下,而点查询则变成了查询[1,x]。

#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=500005; 
template<typename T>void read(T &a)
{
    int f=1,x=0;
    char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch==\'-\')f=0;ch=getchar();
    }
    while(isdigit(ch))
    {
        x=(x<<3)+(x<<1)+ch-\'0\';ch=getchar();
    }
    a=f?x:-x;
}
int n,m;
int tree[maxn];
int a[maxn];
int opt,x,y,k;
int lowbit(int x)
{
    return x&(-x);
}
void add(int x,int y)
{
    for(int i=x;i<=n;i+=lowbit(i))tree[i]+=y;
}
int ans;
int query(int x)
{
    int ans=0;
    for(int i=x;i;i-=lowbit(i))
        ans+=tree[i];
    return ans;
}
int main()
{
    read(n),read(m);
    for(int i=1;i<=n;++i)
        read(a[i]);
    for(int i=1;i<=n;++i)
        add(i,a[i]-a[i-1]);//要注意这里是后一个-前一个,而不是从第一个开始连环减,每一个数代表的都是当前数减前一位数,从1累加才是当前数值
//    for(int i=1;i<=n;++i)cout<<tree[i]<<endl;
//    cout<<"KKKKK"<<endl;
    while(m--)
    {
        read(opt);
        if(opt==1)
        {
            read(x),read(y),read(k);
            add(x,k),add(y+1,-k);
        }
        else{
            read(x);
            printf("%d\\n",query(x));
        }
    }
    return 0;
}
View Code

 

  树状数组还有很多其他应用,比如cdq的时候用。还有二维树状数组,不过窝太弱了,不会这些qwq

 

  

 

  

  

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

浅谈树状数组

浅谈树状数组套主席树

POJ-2155-Matrix二位树状数组应用

浅谈oracle树状结构层级查询测试数据

浅谈前缀和

浅析树状数组