zoukankan      html  css  js  c++  java
  • 浅谈树状数组

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

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

                      

      树状数组就是这个样子的,但是树状数组的空间复杂度是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
    ",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
    ",query(x));
            }
        }
        return 0;
    }
    View Code

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

      

      

      

  • 相关阅读:
    volley框架使用
    Insert Interval
    candy(贪心)
    Best Time to Buy and Sell Stock
    Best Time to Buy and Sell Stock III
    distinct subsequences
    edit distance(编辑距离,两个字符串之间相似性的问题)
    trapping rain water
    word break II(单词切分)
    sudoku solver(数独)
  • 原文地址:https://www.cnblogs.com/yuelian/p/8757091.html
Copyright © 2011-2022 走看看