浅谈树状数组

  还是区间求和区间修改的问题,我们使用线段树解决以后发现编程复杂度比较大

  在这里介绍一个简单的数据结构,树状数组。

  树状数组的优势是编程复杂度小,常数小,时间复杂度也不错

  树状数组的查询,修改,都是LOG(N)级别的

  

       浅谈树状数组

         下面来分析一下上面那个图看能得出什么规律:

         据图可知:c1=a1,c2=a1+a2,c3=a3,c4=a1+a2+a3+a4,c5=a5,c6=a5+a6,c7=a7,c8=a1+a2+a3+a4+a5+a6+a7+a8,c9=a9,c10=a9+a10,c11=a11........c16=a1+a2+a3+a4+a5+.......+a16。

         分析上面的几组式子可知,当 i 为奇数时,ci=ai ;当 i 为偶数时,就要看 i 的因子中最多有二的多少次幂,例如,6 的因子中有 2 的一次幂,等于 2 ,所以 c6=a5+a6(由六向前数两个数的和),4 的因子中有 2 的两次幂,等于 4 ,所以 c4=a1+a2+a3+a4(由四向前数四个数的和)。

        (一)有公式:cn=a(n-a^k+1)+.........+an(其中 k 为 n 的二进制表示中从右往左数的 0 的个数)。

         那么,如何求 a^k 呢?求法如下:  

  
    function lowbit(x:longint):longint; begin exit(x and (-x)) end;     
View Code

  lowbit的返回值就是2^k

答案很简单:2^k=i&(i^(i-1)) ,也就是i&(-i)

下面进行解释:

以i=6为例(注意:a_x表示数字a是x进制表示形式):

(i)_10 = (0110)_2

(i-1)_10=(0101)_2

i xor (i-1) =(0011)_2

i and (i xor (i-1))  =(0010)_2

2^k = 2

C[6] = C[6-2+1]+…+A[6]=A[5]+A[6]

  而我们求和与修改的时候就特别好办了

  
function lowbit(x:longint):longint; begin exit(x and (-x)); end; function getsum(pos:longint):longint; var ans:longint; begin ans:=0; while pos>0 do 

        begin inc(sum,tree[pos]); dec(pos,lowbit(pos)); end; exit(ans); end; procedure modify(pos,delta:longint); begin

    while pos<=n do 

        begin inc(tree[pos],delta); inc(pos,lowbit(x)); end; end;
View Code

  记住,查询区间[l,r]的和时候应该ans:=getsum(r)-getsum(l-1); 

      求数列的前n项和,只需找到n以前的所有最大子树,把其根节点的C加起来即可。不难发现,这些树的数目是n在二进制时1的个数,或者说是把n展开成2的幂方和时的项数。  

  代码如下:

  
const maxn=100010; var val,tree:array [0..maxn] of longint; n:longint; function lowbit(x:longint):longint; begin exit(x and (-x)); end; function getsum(pos:longint):longint; var ans:longint; begin ans:=0; while pos>0 do 

        begin inc(ans,tree[pos]); dec(pos,lowbit(pos)); end; exit(ans); end; procedure modify(pos,delta:longint); begin

    while pos<=n do 

        begin inc(tree[pos],delta); inc(pos,lowbit(pos)); end; end; procedure main; var i,m,l,r:longint; begin read(n); for i:=1 to n do

        begin read(val[i]); modify(i,val[i]); end; read(m); for i:=1 to m do 

        begin read(l,r); writeln(getsum(r)-getsum(l-1)); end; end; begin main; end.
View Code

 

你可能感兴趣的:(树状数组)