• 树状数组


    处理何种问题:常用于动态更新情况下的求区间和,LIS里的求最大值也用到了树状数组。

     

    时间复杂度:单次更新和求和的时间复杂度都是O(logn)

     

    原理:lowbit()函数是整个树状数组的核心,但是在这里我就不说他的理论基础了,只说一下它的功能,例如:给一个原始数组A[]:1,2,3,4,5,6,7,8,9;

    被加工之后就是树状数组C[]:1,3,3,10,5,11,7,36,9;即

    C1 = A1

    C2 = A1+A2

    C3 = A3

    C4 = A1+A2+A3+A4

    C5 = A5

    C6 = A5+A6

    C7 = A7

    C8 = A1+A2+A3+A4+A5+A6+A7+A8

    C9 = A9

    如下图所示:

    树状数组并不是一个二叉树(不像线段树),对于每一个节点,例如C6,我们需要求的是它的父亲节点和前面一个相邻的兄弟节点是哪一个,这些用lowbit()函数都是可以很快捷的找到,即C6的前一个兄弟节点是6-lowbit(6),而他的父亲节点是6+lowbit(6)。

    利用如此操作之后,在动态查询修改之后我们不必对全局进行全部修改,只需要更改节点值就可以了,在log级的时间内完成查询和修改。

    lowbit(x)=(x&(-x));

     

    实现步骤:具体看代码,略

     

    备注:略

     

    输入样例解释

    10  //数组元素个数

    1 2 3 4 5 6 7 8 9 10

    2 1 3  // 2代表查询区间[1,3]之和

    1 3 6  // 1代表给第3个元素加上6

    2 2 7

    1 10 -2

    1 6 3

    2 3 10

     

    输出样例解释

    6 //查询结果

    33

    59

    //对于树状数组,有利有弊,代码也是的确简单,常用在动态更新求区间和
    //当然,某些情况下的求解最大值也是可以做的,代码也的确比线段树好写的多
    //单点更新的操作可以利用Sum找出单点值,再用Add更新两次即可
    
    #include<iostream>
    #include<cstdio>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    
    #define lowbit(x) (x&(-x))
    const int MaxN=10001000;
    long long arr[MaxN];
    int n;
    
    void Build()
    {
        memset(arr,0,sizeof(arr));
        long long temp;
        int j;
        for(int i=1; i<=n; ++i)
        {
            scanf("%lld",&temp);
            j=i;
            while(j<=n)
            {
                arr[j]+=temp;
                j+=lowbit(j);
            }
        }
    }
    void Add(int x,int val)
    {
        while(x<=n)
        {
            arr[x]+=val;
            x+=lowbit(x);
        }
    }
    long long sum(int x)
    {
        long long ans=0;
        while(x>0)
        {
            ans+=arr[x];
            x-=lowbit(x);
        }
        return ans;
    }
    
    
    int main()
    {
        int temp,take;
        while(~scanf("%d",&n))
        {
            Build();//建树
    
            while(scanf("%d",&take))
            {
                if(take==1)//单点添加
                {
                    int x,val;
                    scanf("%d%d",&x,&val);
                    Add(x,val);
                }
                else//求区间和
                {
                    int l,r;
                    scanf("%d%d",&l,&r);
                    printf("%lld
    ",sum(r)-sum(l-1));
                }
            }
    
        }
        return 0;
    }
    

      

  • 相关阅读:
    TTL与RS-485电平转换芯片MAX485/MAX3485
    RS485芯片介绍及典型应用电路
    脉冲电能表的组成及脉冲装置工作原理
    django-redis的安装及使用
    Python折线图——机器人UPtime Trend Chart
    ASP.Net DropDownList控件的使用方法
    C# ASP.Net数据库连接(Oracle)
    django根据已有数据库表生成model类
    Python Outlook发送邮件
    oracle将excel数据导入数据库
  • 原文地址:https://www.cnblogs.com/l1l1/p/9593232.html
Copyright © 2020-2023  润新知