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

  • 相关阅读:
    Gvim快捷键学习
    一步一步学习Vim 全图解释
    经典vim插件功能说明、安装方法和使用方法介绍
    中国数学数字图书馆
    RQNOJ第一题---PID1 / 明明的随机数
    gdb常用命令
    linux c下mysql编程样例
    linux c编程 -- 线程互斥
    linux c多线程编程范例
    数据结构 -- 可重用模块的接口设计模板
  • 原文地址:https://www.cnblogs.com/PPXppx/p/10324232.html
Copyright © 2011-2022 走看看