树状数组的思想复习

树状数组的复习

前言:

学树状数组的时候第一没理解透彻,第二还没写博客用于复习,所以这里写一下用于复习

树状数组:

作用:logn

logn时间实现单点修改区间查询;区间修改单点查询;区间修改区间查询。

但是区间修改区间查询还是线段树好,因为扩展性很强

特点:父子节点关系

例如当前节点为x,那么fa[x]=x+lowbit(x);

基本题型

1.单点修改区间查询

a[i]表示原数组,t[i]表示前缀数组,然后在x<=n的范围内不断地+lowbit(x)更新父亲的t数组即可

那怎么查询呢,

先来个特殊的,1-x的查询,例如x=7时,sum[7]=t[7]+t[6]+t[4] ,我们进一步发现,6=7-lowbit(7),4=6-lowbit(6),所以我们可以通过不断的-lowbit操作来实现求和

那么一段区间的就求差就行了

2.区间修改单点查询

原数组a[i],a[i]差分数组b[i],用上面的方法维护两次单点修改的b数组即可

模板:P3374 【模板】树状数组 1 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
int a[500005];
int t[500005];
int lowbit(int x){
	return x&(-x);
}
void add(int x,int val){
	for(int i=x;i<=n;i+=lowbit(i)){
		t[i]+=val;
	}
}
int ask(int x){
	int ret=0;
	for(int i=x;i>=1;i-=lowbit(i)){
		ret+=t[i];
	}
	return ret;
}
signed main(){
	ios::sync_with_stdio(false);
	cin >> n >> m;
	for(int i=1;i<=n;i++){
		cin >> a[i];
		add(i,a[i]);
	}
	while(m--){
		int a,b,c;
		cin >> a >> b >> c;
		if(a==1){
			add(b,c);
		}
		else{
			cout<<ask(c)-ask(b-1)<<endl;
		}
	}
}
	

区间修改单点查询不也EZ?https://www.luogu.com.cn/record/112034120

特殊题型

求逆序对:P1908 逆序对 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

把一个新的数x放进去之后,求比他小的元素有几个,而比他小(或者是等)的元素个数一定是1到x中存在数的

个数,也就是[1 , x]中有几个数,有点像前缀和了,只不过树状数组t表是的不是前缀和了,t[x]

表示的是[1,x]中有几个数已经存在,这样我们每次把一个新的数x放进去的时候,都需要把包含这

个数的结点更新,然后查询[1,x]有几个数已经存在,换句话说,就是0/1的t数组

同时要注意,我找到了比他小的数字有多少个,但我是要找比他大的啊(逆序)

所以要用i-这个数就是答案(每一次操作的答案)

但肯定是要离散化的,开1e9的数组吃不消啊

代码:记录详情 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

其他练习

练习题详见铃狐sama的竞赛复习计划 - 铃狐sama - 博客园 (cnblogs.com)

posted @ 2023-06-04 17:28  铃狐sama  阅读(3)  评论(0编辑  收藏  举报