zoukankan      html  css  js  c++  java
  • 【学习笔记】树状数组入门

    开始学习树状数组了,还是有些小激动的。

    P.s.本文持续更新

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

    有以下问题

    已知一个长为N的数列,你需要进行下面两种操作共M次:

    1.将某一个数加上x

    2.求出某区间每一个数的和

    数据范围:N<=500000,M<=500000

      怎么做呢?暴力对数组操作?这是可行的,但是N和M都大的可怕,怕是要运行到猴年马月。

      既然无法暴力,那该怎么做呢?这个时候,救世主登场了,它就是:树状数组

      树状数组是什么个东西呢?

    树状数组(Binary Indexed Tree(B.I.T), Fenwick Tree)是一个查询和修改复杂度都为log(n)的数据结构。主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值;经过简单修改可以在log(n)的复杂度下进行范围修改,但是这时只能查询其中一个元素的值(如果加入多个辅助数组则可以实现区间修改与区间查询)。
    这种数据结构(算法)并没有C++和Java的库支持,需要自己手动实现。在Competitive Programming的竞赛中被广泛的使用。树状数组和线段树很像,但能用树状数组解决的问题,基本上都能用线段树解决,而线段树能解决的树状数组不一定能解决。相比较而言,树状数组效率要高很多。

      这次百度终于讲的不是那么玄学了,传送门,可以一看。

     

    如上图,是一个树状数组的基本结构,每一个紫色长条就是树状数组的一个节点。用来维护一些值。

    为什么叫树状数组“树状”呢,是因为树状数组本就是个没有右儿子的二叉树。

    不信?下图可能会更直观一些

    这样子省去右儿子好处主要有两点

      1、节省空间,查询方便

      2、查询速度加快

    坏处主要有一点

      1、相比于线段树有局限性,换句话说,在一些题目上无法使用

    真正的原理实现请戳这。//讲的超级好!!

    再安利一个讲得超级好的网站:传送门(是英文的)

    除此之外,树状数组还有一个坑点:求和时sum函数里如果i=0,会死循环

    总的来说树状数组一般适用于三类问题:

      1,修改一个点求一个区间(点对区间)

      2,修改一个区间求一个点(区间对点)

      3,求逆序列对以及其他特殊情况

    注意:树状数组常常是以一种优化手段出现的,而非主要考点。

    此处给出代码:

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    const int maxn=10000;
    int bit[maxn+1],n;
    int sum(int i)
    {
        int s=0;
        while(i>0)
        {
            s+=bit[i];
            i-=i&-i;//lowbit
        }
        return s;
    }
    void add(int i,int x)
    {
        while(i<=n)
        {
            bit[i]+=x;
            i+=i&-i;
        }
    }
    int main()
    {
        /*do something*/
    }

    -------------------------------------以下是练习题---------------------------------

    洛谷P3374 【模板】树状数组 1

    分析:没错,裸的模板题。

     1 #include <cstdio>
     2 #include <algorithm>
     3 using namespace std;
     4 const int maxn=500050;
     5 int bit[maxn+1],n;
     6 long long sum(int i)
     7 {
     8     long long s=0;
     9     while(i>0)
    10     {
    11         s+=bit[i];
    12         i-=i&-i;
    13     }
    14     return s;
    15 }
    16 void add(int i,int x)
    17 {
    18     while(i<=n)
    19     {
    20         bit[i]+=x;
    21         i+=i&-i;
    22     }
    23 }
    24 int main()
    25 {
    26     int m,d,x,y,p;
    27     scanf("%d%d",&n,&m);
    28     for(int i=1;i<=n;i++)
    29     {
    30         scanf("%d",&p);
    31         add(i,p);
    32     }
    33     for(int i=0;i<m;i++)
    34     {
    35         scanf("%d%d%d",&d,&x,&y);
    36         if(d==1) add(x,y);
    37         else printf("%lld
    ",sum(y)-sum(x-1));
    38     }
    39     return 0;
    40 }
    View Code

    洛谷P3368 【模板】树状数组 2

    分析:题解里kanate_saikou讲得非常好,推荐一看。思想是用差分。坑点:要快读,不然会Tle 3个点

     1 #include <cstdio>
     2 #include <iostream> 
     3 #include <algorithm>
     4 using namespace std;
     5 const int maxn=500005;
     6 int bit[maxn],n,m,a[maxn];
     7 int in()
     8 {
     9     int res=0,x=1;
    10     char c=' ';
    11     while((c<'0'||c>'9') && (c!='-')) c=getchar();
    12     if(c=='-') x=-1;
    13     else res=c-'0';
    14     while((c=getchar())>='0'&&c<='9')
    15     {
    16         res=(res<<3)+(res<<1)+c-'0';
    17     }
    18     return res*x;
    19 }
    20 int sum(int i)
    21 {
    22     int sum=0;
    23     while(i>0)
    24     {
    25         sum+=bit[i];
    26         i-=i&-i;
    27     }
    28     return sum;
    29 }
    30 int add(int i,int x)
    31 {
    32     while(i<=n)
    33     {
    34         bit[i]+=x;
    35         i+=i&-i;
    36     }
    37 }
    38 int main()
    39 {
    40     n=in();m=in();//printf("%d
    %d",n,m);
    41     for(int i=1;i<=n;++i) {
    42         a[i]=in();
    43         add(i,a[i]-a[i-1]);   //直接将树状数组存为差分后数列 #**b[i]=a[i]-a[i-1]**#
    44     }
    45     for(int p=1;p<=m;++p) {
    46         int op,x,y,k;
    47         op=in();
    48         if(op==1) {
    49             x=in();y=in();k=in();
    50             add(x,k); add(y+1,-k);   // #**b[x]+=d,b[y+1]-=d**#
    51         }
    52         if(op==2) {
    53             x=in();
    54             int ans=sum(x);   //#**bit[i]=sigma(k=1 to i) b[k]**#
    55             cout<<ans<<endl;
    56         }
    57     }
    58     return 0;
    59 }
    View Code
  • 相关阅读:
    ros 无线WDS MESH配置一(这个不好用,没有方法二好用)
    ROS脚本编辑器Notepad++装一个mikrotik的插件 RouterOS-Syntax-highliter
    关于ip-neighbor里的mac telnet的一些说明
    ros无线2G和5G的带宽简单计算
    ros的AP创建20个虚拟AP-VAP的脚本
    python异常值(outlier)检测实战:KMeans + PCA + IsolationForest + SVM + EllipticEnvelope
    str.replace替换变量名的字符串
    pd.ExcelWriter(to_excel)保存结果到已存在的excel文件中
    pandas.DataFrame.dropna删除缺失值
    pandas.DataFrame.astype数据结构转换
  • 原文地址:https://www.cnblogs.com/noblex/p/7633337.html
Copyright © 2011-2022 走看看