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

  • 相关阅读:
    查询数据库中的相同值得所有表跟字段【存储过程】
    一些常用的SQL语句
    添加网站本地映射
    ReSharper 2016.3.2 Ultimate 官方最新破解版
    C# 利用VS自带的WSDL工具生成WebService服务类
    Linux环境下docker搭建wordpress应用
    Appium环境搭建
    内联以及外联css,js文件的理解
    前端雅虎23条理解
    docker安装和使用
  • 原文地址:https://www.cnblogs.com/PPXppx/p/10324232.html
Copyright © 2011-2022 走看看