zoukankan      html  css  js  c++  java
  • 树状数组(学习笔记)

    树状数组

    树状数组的基本用途是维护序列的前缀和,相比前缀和数组,树状数组优势在于高效率的单点修改,单点增加(前缀和数组单点修改效率比较低)

    因为树状数组的思想,原理还是很好理解的,就直接讲基本算法;

    1 lowbit函数

    关于lowbit这个函数,可能会有点难以理解,但其实你不理解也没关系,把模板背下来就好

    根据任意正整数关于2的不重复次幂的唯一分解性质,例如十进制21用二进制表示为10101,其中等于1的位是第0,2,4(最右端是第0位)位,即21被二进制分解成(2^4+2^2+2^0);

    进一步地,整个区间[1,21]可以分成如下3个小区间:

    长度为(2^4)的小区间[1,(2^4)];

    长度为(2^2)的小区间[(2^4+1),(2^4+2^2)];

    长度为(2^0)的小区间[(2^4+2^2+1),(2^4+2^2+2^0)];

    对于给定的初始序列A,我们可以建立一个数组c,c[x]表示序列A的区间[x-lowbit(x)+1,x)]中所有数的和;

    int lowbit(int x){return x&-x;}
    

    2 单点增加操作

    void update(int x,int y){
    	for(;x<=n;x+=lowbit(x))
        	c[x]+=y;
    }
    

    3 查询前缀和

    int sum(int x){
    	int ans=0;
        for(;x;x-=lowbit(x)) ans+=c[x];
        return ans;
    }
    
    

    4 扩展

    上述查询前缀和是统计[1,x]的前缀和,若要统计区间[x,y]的和,则调用sum函数即可:sum(y)-sum(x-1);

    多维树状数组:

    (扩充为m维)将原来的修改和查询函数中的一个循环,改成m个循环m维数组c中的操作;

    (n*m)的二维数组为例:

    将(x,y)的值加上z,不是把区间[x,y]中的每个值加z
    int update(int x,int y,int z){
    	int i=x;
        while(i<=n){
        	int j=y;
            while(j<=m){
            	c[i][j]+=z;
                j+=lowbit(j);
            }
            i+=lowbit(i);
        }
    }
    
    int sum(int x,int y){
    	int res=0,i=x;
        while(i>0){
        	int j=y;
            while(j>0){
            	res+=c[i][j];
                j-=lowbit(j);
            }
            i-=lowbit(i);
        }
        return res;
    }
    
    

    注意树状数组的下标绝对不能为0,因为lowbit(0)=0,这样会陷入死循环

    两道模板题,多打打模板~~

    https://www.luogu.org/problemnew/show/P3374
    https://www.luogu.org/problemnew/show/P3368

  • 相关阅读:
    首篇
    typedef 的几种用法
    ftp 命令
    (zt)STL中的map与hash_map
    (zt)关于UDP网络游戏服务器的一些探讨
    (zt)UDP编程的时候,一次发送多少bytes好?
    (zt)界面技术概述
    (zt)这是对目前大部分平台都适用的内存对齐规则的定义
    (zt)高性能I/O设计模式Reactor和Proactor
    (zt)ACE高效PROACTOR编程框架一ClientHandle
  • 原文地址:https://www.cnblogs.com/PPXppx/p/10324232.html
Copyright © 2011-2022 走看看