树状数组简单总结(原理见以往帖子)

30 篇文章 0 订阅
3 篇文章 0 订阅

树状数组:快速(动态)求前缀和
在线算法(支持修改)

O(logn)     

1:给某个位置上的数加上一个增量(修改某个元素)
2:求某个前缀和 


即:单点修改,区间查询(sum(r)-sum(l-1))

树状数组只能支持这俩问题,别的问题都得通过转化才能解决
比如区间修改,单点查询
需要利用差分转化为区间修改

记树状数组为C[x],定义:
C[x]=(x-lowbit[x],x],即x-lowbit[x]+1~x,奇数为其本身
lowbit[x]=x&-x,含义是2^k,k为x的二进制表示末尾有k个0
树状数组可以画成一棵树(每个节点只有一个父节点),x的父节点为x+lowbit(x),x的子节点为x-lowbit(x)


求前缀和很简单,递归找子节点累加即可:C[x]+=C[x-lowbit[x]],递归边界是x-lowbit[x]=1

int add(int x){
    int res=0
    for(int i=x;!i;i-=lowbit(x))    res+=c[i];
    return res;
}

单点修改的话需要修改这个点的所有父节点,x的父节点为x+lowbit(x)

void query(int x,int v){
    for(int i=x;i<=N;i+=lowbit(x))    c[i]+=v;
}

初始化:c[i]+=a[i]
单点修改(c[i]改成c) c[i]+=c-c[i]

求逆序对:(以下部分转载自下面帖子)

 树状数组(求逆序对)_baby的我的博客-CSDN博客_树状数组求逆序对

先离散化

#include<bits/stdc++.h>
#define M 500005
using namespace std;
int a[M],d[M],t[M],n;
//原数组/ 离散化后的数组/ 树状数组 
bool cmp(int x,int y)
{
	if(a[x]==a[y]) return x>y;//避免元素相同 
	return a[x]>a[y];//按照原序列第几大排列 
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	cin>>a[i],d[i]=i;//初始化
	
	sort(d+1,d+n+1,cmp);
	//排序时候d就是离散化的数组了 
 
        return 0;
} 

转换之后,空间复杂度就没这么高了,但不是求d中的逆序对了,而是求d中的正序对

原因是离散化的数组存的是第i的数在什么位置

比如原序列a={5,3,4,2,1}

首先把1放到树状数组t中,此时t只有一个数1,t中比1小的数没有,sum+=0
再把3放到树状数组t中,此时t只有两个数1,3,比3小的数只有一个,sum+=1
把2放到树状数组t中,此时t只有两个数1,2,3,比2小的数只有一个,sum+=1
把4放到树状数组t中,此时t只有两个数1,2,3,4,比4小的数有三个,sum+=3
把5放到树状数组t中,此时t只有两个数1,2,3,4,5,比5小的数有四个,sum+=4
最后算出来,总共有9个逆序对,可以手算一下原序列a,也是9个逆序对,
 

再求和

树状数组t表是的不是前缀和了,t[x]表示的是[1,x]中有几个数已经存在,这样我们每次把一个新的数x放进去的时候,都需要把包含这个数的结点更新,然后查询[1,x-1]有几个数已经存在

还是拿上一个例子:

把1放进去,包含t[1]的结点t[1]++,t[2]++、t[4]++, 由于n==5,算到t[8]的时候就已经跳出,查询[1,1-1]中比他小的数为0
把3放进去, 包含t[3]的结点t[3]++,  t[4]++  ,然后查询[1 , 3-1]中有几个数已经存在,t[2]==1, sum+=1,lowbit一发等于0跳出
把2放进去, 包含t[2]的结点t[2]++,  t[4]++ , 然后查询[1 , 2-1]中有几个数已经存在,t[1]==1, sum+=1,lowbit一发等于0跳出
把4放进去,包含t[4]的结点t[4]++,   t[8]大于n 跳出, 查询[1 , 4-1]中有几个数已经存在,t[3]==1, sum+=1,lowbit一发t[2]==2,所以sum+=2
把5放进去, 包含t[5]的结点t[5]++,   t[6]大于n跳出 , 查询[1 , 5-1]中有几个数已经存在,t[4]==4, sum+=4,lowbit一发等于0跳出
最后答案就出来了,关键是要理解那句标了红色的那句话,不是前缀和,而是有几个数已经存在,假如a[8]等于4,那么就表示[1,8]中只有4个数存在。
 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值