• [学习笔记] 树状数组


    最近沉迷文化课,好久没有研究新算法了(也就那么十来天吧,嗯。),而且还有一大堆题都还没订正,趁着现在一点时间就学了一下一个叫做树状数组的基(gao)础(shen)数据结构(是不是觉得我特别菜,嗯,我自己都这么觉得)。好了,不和大家扯淡了,开始正题——树状数组!

    树状数组这种东西,感觉功能和线段树差不多(我认为),只是它不能支持区间修改(其实如果线段树不打Lazy tag的也不支持区间修改)。树状数组支持的操作有:1、单点修改 2、区间查询

    算法核心思想:

    个人认为,其实树状数组的算法核心就是二进制拆分,对于一个数比如13,13的二进制就是1101,那么13=2^{3}+2^{2}+2^{0},换个角度看是不是可以把1-13分为三个区间:[1,2^{3}],[2^{3}+1,2^{3}+2^{2}],[2^{3}+2^{2}+1,2^{3}+2^{2}+2^{0}],然后一种叫做树状数组的神奇数据结构就诞生啦!!!(鼓掌)树状数组张这样:

    这个时候顺着图,假如说,我们要查询[1-7]这一区间应该怎么办呢?

    我们知道,7=2^{2}+2^{1}+2^{0},也就是说可以分为,[1,2^{2}],[2^{2}+1,2^{2}+2^{1}],[2^{2}+2^{1}+1,2^{2}+2^{1}+2^{0}] 这三个区间,结合上图就是c[4],c[6],c[7],这仨茬儿,也就是c[2^{2}],c[2^{2}+2^{1}],c[2^{2}+2^{1}+2^{0}]。发现了啥?——这TM不就是二进制拆分吗!!!

    那么区间[1,5] 呢?c[4]和c[5]呗。[1,6]呢?c[4]和c[6]呗!

    就问你这东西简不简单?——简单(个毛线啊)!!!

    关于快速地进行“二进制拆分”

    如果我们按照朴素的方式进行二进制拆分,其实会很浪费,因为我们只需要二进制中为“1”的项就可以了,所以在这里给大家介绍一种叫做lowbit的神奇操作:

    作用是求二进制中最后的1附带上后面所有0构成的数的数值。

    具体操作为:n and (~n+1)=n and (-n) (其中“~”为逐位取反)

    那么我们如何来证明这个操作的正确性呢?

    首先我们设一个非负整数n在二进制表示下第k位为1,且第k位以后都是0,n=x...xx100...00。

    所以n取反后变为n'=x'...x'x'011...11。再加1,n''=x'...x'x'100...00。

    最后n and n''=100...00。

    另外在补码表示下,~n=-1-n这应该是“常识”吧,就不多提了。(我可不会告诉你是因为我也不知道才这么写的

    所以有了这个lowbit操作后,我们就可以通过不断的让"x-lowbit(x)"来获取[1,x]在树状数组中所要加的区间了。

    关于更新操作

    更新操作其实和我们上文一直在讨论的查询操作差不多,可以结合上图——呃,好吧知道你们懒得在往上翻了,那就再插一遍吧。好,我们结合下图,嗯,下图,继续。

    这个时候,树状数组这样构造的另一个特征就不得不被提及了——c[x]的父亲结点为c[x+lowbit(x)],所以就很方便我们对这个树形结构进行更新,只需要递归进行就ok了。

    关于查询操作

    查询操作其实在我们讲核心思想的时候就把他给讲完了,最后要提的一点就是:我们不能直接求出[L,R]这一区间,而需要通过[1,R]-[1,L-1]来进行。

    The End

    嗯,上代码吧!!!

     1 var
     2   a,c:array[0..100000]of longint;
     3   n,m,i,x,y,tip:longint;
     4 procedure update(x,y:longint);
     5 begin
     6   while x<=n do
     7   begin
     8     c[x]:=c[x]+y;
     9     x:=x+x and(-x);
    10   end;
    11 end;
    12 function sum(x:longint):longint;
    13 begin
    14   sum:=0;
    15   while x>0 do
    16   begin
    17     sum:=sum+c[x];
    18     x:=x-x and(-x);
    19   end;
    20 end;
    21 begin
    22   read(n,m);
    23   for i:=1 to n do
    24   begin
    25     read(a[i]);
    26     update(i,a[i]);
    27   end;
    28   for i:=1 to m do
    29   begin
    30     read(tip,x,y);
    31     if tip=1 then update(x,y) else writeln(sum(y)-sum(x-1));
    32   end;
    33 end.
  • 相关阅读:
    CF710F String Set Queries(AC自动机+二进制分组)
    P5231 [JSOI2012]玄武密码(AC自动机)
    AC自动机基础&应用
    [SDOI2011]计算器(快速幂,线性同余方程,BSGS)
    数论——欧拉定理和费马小定理
    AtCoder Beginner Contest 173 题解
    【CSP2019】树的重心(树的重心、倍增、换根)
    CF708C Centroids(换根dp,树的重心)
    凸包(Graham与Andrew算法求凸包)
    [USACO10MAR]Great Cow Gathering G(换根dp)
  • 原文地址:https://www.cnblogs.com/WR-Eternity/p/9723126.html
Copyright © 2020-2023  润新知