线段树/树状数组(HDU1754/HDU1166)

线段树

线段树是一种二叉树结构

一个问题,只要能化成对一些“连续点”的修改和统计问题,基本就可以用线段树来解决了

总原理:

将[1,n]分解成若干特定的子区间(数量不超过4*n)

用线段树对“编号连续”的一些点,进行修改或者统计操作,修改和统计的复杂度都是O(log2(n))(使用线段树可以快速查找某一节点在若干条线段中出现的次数)

用线段树统计的东西,必须符合区间加法,(也就是说,如果已知左右两子树的全部信息,比如要能够推出父节点);否则,不可能通过分成的子区间来得到[L,R]的统计结果。

父亲为I。左儿子是2i,右儿子是2i+1;

以hdu1754为例

1. 建树(上->下)

void buildtree(int i,int left,int right){
 node[i].leftt=left;
 node[i].rightt=right;
 node[i].value=0;
 if(left==right){
  index[left]=i;
  return; 
 }
 buildtree(i*2,left,(int)(floor(left+right)/2.0));
 buildtree(i*2+1,(int)(floor(left+right)/2.0)+1,right);
}

//index是记录该节点信息的下标

2. 更新(下->上)

void updatetree(int ri){
 if(ri==1) return;
 int fi=ri/2;
 int a=node[fi<<1].value;
 int b=node[(fi<<1)+1].value;
 node[fi].value=max(a,b);
 updatetree(ri/2);
}

3.查询(上->下)

void query(int i,int left,int right){
 if(node[i].leftt==left&&node[i].rightt==right){
  MAX=max(MAX,node[i].value);
  return;
 }
 i=i<<1;
 if(left<=node[i].rightt){
  if(right<=node[i].rightt)
   query(i,left,right);
  else query(i,left,node[i].rightt);
 }
 i++;
 if(right>=node[i].leftt){
  if(left>=node[i].leftt) query(i,left,right);
  else query(i,node[i].leftt,right);
 }
}

整体代码

int main(){
 int n,m,q;
 //ios::sync_with_stdio(false); 
 while(cin>>n>>m){
  buildtree(1,1,n);
  for(int i=1;i<=n;i++){
   cin>>q;
   node[index[i]].value=q;
   updatetree(index[i]);
  }
  char op;
  int a,b;
  while(m--){
   cin>>op>>a>>b;
   if(op=='Q'){
    MAX=0;
    query(1,a,b);
    cout<<MAX<<endl;
   }
   else{
    node[index[a]].value=b;
    updatetree(index[a]);
   }
  }
 }
 return 0;
}

树状数组

什么是树状数组

在这里插入图片描述
黑色数组代表原来的数组(下面用A[i]代替),红色结构代表我们的树状数组(下面用C[i]代替),发现没有,每个位置只有一个方框,令每个位置存的就是子节点的值的和,则有

C[1] = A[1];
C[2] = 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];
可以发现,这颗树是有规律的
C[i] = A[i - 2k+1] + A[i - 2k+2] + … + A[i]; //k为i的二进制中从最低位到高位连续零的长度
例如i = 8(1000)时候,k = 3,可自行验证。

这个怎么实现求和呢,比如我们要找前7项和,那么应该是SUM = C[7] + C[6] + C[4];
而根据上面的式子,容易的出SUMi = C[i] + C[i-2k1] + C[(i - 2k1) - 2k2] + …;
其实树状数组就是一个二进制上面的应用。
即 2^k=i&(-i)
而且这个有一个专门的称呼,叫做lowbit,即取2^k

建立树状数组

在这里插入图片描述

int lowbit(int x){
 return x&(-x);
} 
void update(int i,int k){
 while(i<=n){
  c[i]+=k;
  i+=lowbit(i);
 }
}
int getsum(int i){
 int res=0;
 while(i>0){
  res+=c[i];
  i-=lowbit(i);
 }
 return res;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值