zoukankan      html  css  js  c++  java
  • 树状数组

    主要解决的问题

    对于n个数,有修改和查询操作

    单点修改区间查询(对于第i个数增加或减少一个值,然后求一个区间的值,这个区间也可以是一个点)

    区间修改单点查询 (对于一个区间都增加或减少一个值,然后求一个点的值,这类题我们让每个点先记录的是和前面值的差,那么一个点的前缀和就是这个点的值,对于修改一个区间,只需要这个区间最左面的点加上这个值,最后面+1那个点减去这个值,这样对于每个点的前缀和,只有这个区间里的点有改变,所以整体操作还是只修改了一个点)

    代码量比线段树少很多,但是解决的问题也少很多。

    其中0号点不能存值

    a[i]存每个点的值,f[i]存树状数组每个点的值

    Lowbiti=x&(-x)  是计算一个数最低位1所代表的值,例如lowbit(6)=2因为11010=2

    操作有两个

    Add(i,v)   i号点增加v

    Su(i)      i个点的值求和

    在树状数组中数组的每个点记录的并不是某个点的值,它记录的是某个点二进制下,去掉最低位1然后加1,一直到本个数之间的值,例如12号点,他的二进制是1100,他其实记录的是10011010,1011,1100的值的和

    对于i点进行add操作,最初和更新后的每一个i=i+lowbit(i)&&i<=nf[i]都要增加值v

    所以反过来看的话 a数组中1010增加v的话,f数组中1010,1100,10000.....都要增加v(省略号有多少,就要看整体的数到底有多少,n的大小)

    因为对于n最多有lgn位,操作最多就是lgn次

    su就是求前缀和,例如1100只能求出1000+1~1100的数,所以还差0+1~1000,而些数恰好在1000中,容易看出,只要求出每一个i=i-lowbit(i)&&i!=0f[i]值之和即可

    因此也跟1的个数有关,所以操作也是lgn次

    int lowbit(int x)

    {

        return x&(-x);

    }

    void add(int x,int v)

    {

        while(x<=n)

        {

            f[x]+=v;

            x+=lowbit(x);

        }

    }

    int su(int x)

    {

        int sum=0;

        while(x)

        {

            sum+=f[x];

            x-=lowbit(x);

        }

        return sum;

    }

    树状数组还可以进行二维的修改的查询,例如对于一个矩阵要修改一个区间,然后查询一个点,就可以同样用前缀的思想,每个点到源点形成的小矩阵记录这个点a[i][j]的值

    那么修改一个矩阵(x,y)(xx,yy),若0<x<=xx,0<y<=yy, 只需要用add函数把(x,y),(xx+1,yy+1)增加v,(x,yy+1),(xx+1,yy)减去v即可(1维数组改两个点,二维改4...

    对于n*m的矩阵

    void add(int x,int y,int v)

    {

        int yy=y;

        while(x<=n)

        {

            y=yy;

            while(y<=m)

            {

               f[x][y]+=v;

               y+=lowbit(y);

            }

            x+=lowbit(x);

        }

    }

  • 相关阅读:
    (转载)MP3 编码解码 附完整c代码
    (转载) 一个大概的框架
    (转载)音视频相关的书籍,多媒体技术
    js原生选项卡切换
    点击div删除div本身
    鼠标移入旋转动画
    鼠标移入,改变border颜色
    小白初建博客!
    java设计模式--六大原则
    java设计模式--策略模式
  • 原文地址:https://www.cnblogs.com/lmhyhblog/p/10263322.html
Copyright © 2011-2022 走看看