树状数组 模板

来源:互联网 发布:app数据抓取 编辑:程序博客网 时间:2024/06/02 05:28

早就想着把以前搞acm的算法复习一遍,那可是瑰宝啊,一直没时间,之前面实习就有此感慨,以前学的算法都忘了,明日要去面个不知名的公司,准备下吧,先把树状数组和TRIE图复习下,明天凑合应对。

不多说,上问题:

给你一个数组a[N],假设存int数,对这个数组的操作有2种:(1)修改数组中某元素的值(2)查询该数组任意区间的和。见poj2481.

最朴素想法:在原数组上操作,对于每个操作(1),修改该位置的值,对于(2)操作,遍历数组求和,复杂度为M+Q*N(M为修改次数,Q为查询次数,N为数组size),当N很大时无法忍受。

树状数组解决此问题的复杂度为:(M+Q)*log2(N);

原理:查询某区间[a,b]的和等价于求解sum(b)-sum(a-1)(sum(k)为[1,k]的和);故转化为求解前k项和的问题了。联想线段树分段处理的思想,此处不是二分,而是另一种分段方法。其实可用线段树解决之,但没有树状数组处理简单。开另一数组C[N],元素C[k]存储的值为区间[k-lowbit(k]+1,k]的和。lowbit(k)=k表示成二进制后最右边的位代表的十进制数,即k能整除的最大的(2^x);举个例子:6,表示成2进制为00000110,最右边的位为第2位,故lowbit=2^(2-1)=2,6能整除的2的次方为2。上经典图:

 

C数组构成一个树状关系:C[x]分布在 log2(lowbit(x)) 层;每个C[k]只有一个父亲节点,子节点与其父节点之间的距离为lowbit(k)。

对于操作(1),修改某位置k的值,则对于数组C要修改包括a[k]的位置,k与下一个相关节点的距离为lowbit(k)。

对于操作(2),查询某位置k的sum(k),则一次查询包括k到1的sum的位置,k与其下一个相关节点的距离为lowbit(k)。

由图可知修改和查询的复杂度都为log2(N);

求lowbit很巧妙:x&(x^(x-1));

x:   (k1个0或1)1(k2个0)

x-1:   (k1个0或1)0(k2个1)

x^(x-1):       (k1个0)1(k2个1)

x&(x^(x-1))      (k1个0)1(k2个0)//即得lowbit(x);

看了下另外一个自己写的源码:原来还有一种更简单的lowbit求法:lowbit = x&(-x);

x:    (k1个0或1)1(k2个0)

~x:    (k1个1或0)0(k2个0)

-x=~x+1:          (k1个1或0)1(k2个0)

x&(-x)             (k1个0)1(k2个0)//即得lowbit(x);

类似巧妙的还有:判断一个数是否为2的次方if((x&-x)==x);

代码如下:


int lowbit(int x)//位运算求lowbit(x)
{
 return x&(x^(x-1));
}

void add(int x,int val)//修改位置x的值,val为变化值,可为+-;
{
 while(x <= Max)
 {
  c[x] += val;
  x += lowbit(x);
 }
}

int getsum(int x)//求sum(x)
{
 int result = 0;
 while(x > 0)
 {
  result += c[x];
  x -= lowbit(x);
  
 }
 return result;
}可升级为2维及多维:修改2维矩阵某个值,求区间[x1,y1]~[x2,y2]的和。类似,代码如下:int lowbit(int x)
{
 return x&(-x);
}

void add(int x,int y,int det)
{
 int i,j;
 for(i=x;i<=n;i+=lowbit(i))
  for(j=y;j<=n;j+=lowbit(j))
   c[i][j]+=det;
}

int getsum(int x,int y)
{
 int i,j,sum=0;
 for(i=x;i>0;i-=lowbit(i))
  for(j=y;j>0;j-=lowbit(j))
   sum+=c[i][j];
 return sum;
}


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/tmac_0817/archive/2011/04/23/6347438.aspx

1 0
原创粉丝点击