zoukankan      html  css  js  c++  java
  • 树状数组入门(转)

    原博客树状数组

    1 一维树状数组
    这里写图片描述

    1 什么是树状数组
    树状数组是一个查询和修改复杂度都为log(n)的数据结构,假设数组A[1..n],那么查询A[1]+…+A[n]的时,间是log级别的,而且是一个在线的数据结构。

    2 树状数组作用
    我们经常会遇到动态连续和查询问题,给定n个元素A[1~N],让我们求sum[L,R] = A[L]+…+A[R],或者更改A[i]的值。

       假设数据很小的时候,那么我们利用暴力就可以搞定,这个时候更改A[i]的复杂度为O(1),但是求和的复杂度为O(n),如果有m次求和就是O(n*m),但是m很大的时候这个方法显然是不能够满足效率的要求。这个时候我们引入树状数组,树状数组的求和和更新都是O(logN),所以大大的降低了复杂度。
    

    3 具体分析

     1 建立树状数组就是先把A[] 和 C[]清空,然后假设有n个数那么就是做n次的update()操作就是建立树状数组,所以总的时间复杂度为O(nlogn)。
    
     2 设原数组为A[1..N],树状数组为c[1..N],其中c[k] = A[k-(2^t)+1] + ... + A[k]。比如c[6] = A[5] + A[6]。
    
        假设 A为被计数数组,C为树状数组(计数)
    
        0000 0001:C1 = A1
        0000 0010:C2 = A1 + A2
        0000 0011:C3 = A3
        0000 0100:C4 = A1 + A2 + A3 + A4
        0000 0101:C5 = A5
        0000 0110:C6 = A5 + A6
        0000 0111:C7 = A7
        0000 1000:C8 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8
        ...
        0001 0000:C16 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8+ A9 + A10 + A11 + A12 + A13 + A14 + A15+ A16
    
    
    
     3 也就是说,把k表示成二进制1***10000,那么c[k]就是A[1***00001] + A[1***00010] + ... + A[1***10000] 这一段数的和。
    
    
    
     4 设一个函数lowbit(k)为取得k的最低非零位,容易发现,根据上面的表示方法,从A[1]到A[k]的所有数的总和即为
        sum[k] = c[k] + c[k-lowestbit(k)] + c[k-lowestbit(k)-lowestbit(k-lowestbit(k))] + ... 于是可以在logk的时间内求出sum[k]。
    
    
    
     5 当数组中某元素发生变化时,需要改动的c值是c[k],c[k+lowestbit(k)], c[k+lowestbit(k)+lowestbit(k+lowestbit(k))] ... 这个复杂度是logN (N为最大范围)
    
    
    
     6 如果题目要求sum[L , R] = sum[R]-sum[L-1]
        sum[L-1] = A[1]+A[2]+...+A[L-1]
        sum[R] = A[1]+A[2]+...+A[L]+...+A[R]
        sum[R]-sum[L-1] = A[L]+A[L+2]+...+A[R]
    
    
    
     7 树状数组的下标严格从1开始,所以如果出现0的情况要注意加1.(因为lowbit(0)是0所以如果出现为0的时候会进入无限循环中) , 树状数组中的每个元素至少含有它本身的一个值。
    

    3 树状数组的两类操作

    1 单点更新,区间求和
    
       1 一维树状数组,单点更新,区间求和
    
       比如要更新点x ,x点的值加上val即调用add(x , val) , 求区间[1 , x]的和即为getSum(x)
    
    int lowbit(int x){  
        return x&(-x);  
    }  
    
    int getSum(int x){  
        int sum = 0;  
        while(x){  
            sum += treeNum[x];  
            x -= lowbit(x);  
        }  
        return sum;  
    }  
    
    void add(int x , int val){  
        while(x < MAXN){  
             treeNum[x] += val;  
             x += lowbit(x);  
        }  
    }  

    2 区间更新,单点求和
    1 一维树状数组

        更改区间[x , y],区间[x , y]里面的每个数全部加上val , 查询点k的值
    
        区间[x , y]加上val相当于点x加上val , 点y+1减去val,那么求k点的值就等于[1,k]的和
    
    int lowbit(int x){  
        return x&(-x);  
    }  
    
    int getSum(int x){  
        int sum = 0;  
        while(x){  
            sum += treeNum[x];  
            x -= lowbit(x);  
        }  
        return sum;  
    }  
    
    void add(int x , int val){  
        while(x < MAXN){  
             treeNum[x] += val;  
             x += lowbit(x);  
        }  
    }  
    
    void solve(){  
        // 把区间[x , y]每一点加上val  
        add(x , val);  
        add(y+1 , -val);  
        // 计算点k的值  
        int num = getSum(k);  
    }  
    
    

    5 常用的技巧

    假设初始化数组每个点的值为1,那么我们知道对于一维的树状数组来说,我们知道treeNum[i] = lowbit(i) . 对于二维树状数组来说treeNum[i][j] = lowbit(i)*lowbit(j)

    void init(){  
        memset(treeNum , 0 , sizeof(treeNum));  
        for(int i = 1 ; i < MAXN ; i++){  
            num[i] =1;  
            treeNum[i] = lowbit(i);  
        }  
  • 相关阅读:
    xCHM 1.11
    Fluxbox 1.0 RC 3
    Money Manager Ex:个人理财软件
    K3b 1.0 变化了什么?
    Kbfx:KMenu 的替换品
    Semantik:思想导图绘制软件
    新手入门:了解邮件服务与相关协议
    用 GDI 操作 EMF 文件[2]: PlayEnhMetaFile、DeleteEnhMetaFile
    WinAPI: WritePrivateProfileString、GetPrivateProfileString 简单读写 Ini 文件
    一毫米等于多少像素? GetDeviceCaps
  • 原文地址:https://www.cnblogs.com/stevensonson/p/7612207.html
Copyright © 2011-2022 走看看