NYOJ 116 树状数组

本文深入解析树状数组的概念、工作原理及其实现方法,包括低比特函数、求和函数、修改函数,并通过一道典型题目展示其在解决实际问题中的高效应用。同时,提供了AC代码实现,以及对复杂度的对比分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

初学树状数组。。

 

 

这是线段树的图,由图可知,c[1]=a[1];c2=a[1]+a[2];c[3]=a[3];c[4]=a[1]+a[2]+a[3]+a[4];c[5]=a[5];

c[6]=a[5]+a[6];c[7]=a[7];c[8]=a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]+a[8];

可以得到这个规律,i为奇数时,c[i]=a[i],这里1,3,5,7便是例子。偶数时这里就跟2的幂有关系了,i的因子中最多有2的多少次幂,例如4,2的2次幂,为4,所以从a[4]数共4个数的值,即a[1],a[2],a[3],a[4],当i=6时,因子数最大为2,所以数2个数,即a[5],a[6],同理i=8时,2的三次幂,等于8,即a[1]+.....a[8]的值.网上查询可知,公式为

c[n]=a(n-a^k+1)+......+a[n]; (其中k为从右往走数0的个数,也即第一个出现1的位置,起点从0开始)

这里可以用一个函数计算得

int lowbit(int x)

{

return x&(-x);

}

这个函数怎样理解呢?

x=1时,x的二进制表示为(假设位数为8) 0000 0001

     -x的二进制表示为           1111 1111

                        --->  0000 0001 =1;

x=6时  x的二进制表示为           0000 0110

     -x的二进制表示为           1111 1010

                        --->  0000 0010 =2;

这里相信你明白了,若不懂负数的二进制表示,可百度查之

这个函数的目的就是求出2^k (k为往右数第一个出现1的位置起点从0开始或者0出现的个数,见上)

接下来就是求和了,

int Sum(int n)

{

   int sum=0;

   while(n>0)

{

    sum+=c[n];

    n=n-lowbit(n);

}

return sum;

}

这里具体该怎样理解呢?我们可以看成求一段路径的和,不同于普通的一维数组按照下标为1一次递增或递减,这里是按照树的结构,每次logn,即求路径(x,x-lowit(x),....1)为下标的和

最后修改某个结点的值

void change(int i,int x)

{

  while(i<=m)           //m个结点的总和

  {

      c[i]=c[i]+x;

      i=i+lowbit(i);

  }

}

如果上面求和理解了,这步应该很快理解了,改变了某个结点的值,那么对应的路径上相应的点也要改变,结束的条件即为到达根结点。

下面来看看这道题

链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=116

1,若采取常规的方法,求解,时间的复杂度为O(n^2);

2,采取树状数组的方法,AC代码:

#include<iostream>
#include<stdio.h>
#include<cstring>
using namespace std;
int c[1000008],m,Q,t;
int lowbit(int x)
{
 return x&(-x);
}
int Sum(int n)
{
 int sum=0;
 while(n>0)
 {
  sum=sum+c[n];
  n=n-lowbit(n);
 }
 return sum;
}
void change(int i,int x)
{
 while(i<=m)
 {
  c[i]=c[i]+x;
  i=i+lowbit(i);
 }
} 
int main()
{
 scanf("%d%d",&m,&Q);
 char key[10];
 int start,end;
 for(int i=1;i<=m;++i)
 {
   scanf("%d",&t);
   change(i,t);
 }
    while(Q--)
    {
  scanf("%s%d%d",key,&start,&end);
     if(key[0]=='Q')
     printf("%d\n",Sum(end)-Sum(start-1));
     else if(key[0]=='A')
     change(start,end);
 }
 return 0;
}

参考资料来源:http://www.cnblogs.com/zhangshu/archive/2011/08/16/2141396.html

                            http://hi.baidu.com/czyuan_acm/item/b14bff6ab6ffd093c5d249db

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值