树状数组的基本操作

不得不说,树状数组很优雅,网上前几篇博文写的很容易理解,在这里主要是记录下自己的学习。

先来张图,方便理解


树状数组最主要的几个部分,

一是求lowbit,

一个正数的相反数的二进制是正数的二进制取反加1,因此它们之间进行&运算,便得到了lowbit——一个数二进制的只保留一个最低位的1,高位的1全部清空。

int lowbit(int k){
	return k&-k;
}
通过原数组a构建树状数组c,之后便维护c数组就ok。

所以,现在需要看a数组和c数组的联系,c数组的某个元素其实是若干个a数组中的元素的和,c[i]有lowbit(i)个a数组元素构成,即从a[i]开始然后往前找lowbit(i)个,然后相加得和即为c[i]的值。

所以构建c数组的代码如下

void build(int jk[], int n){
	int i, k, res, j;
	for(i = 1; i <= n; ++i){
		k = lowbit(i);
		res = 0, j = i;;
		while(k){
			res += jk[j];
			--j, --k;
		}
		c[i] = res;
	}
}

仔细看看图,其实能够知道,c[i]都是只会影响到c[i]+lowbit(i),因此更新完单点的数值之后,

向上上溯更新能够影响到的点。

void update(int value, int k, int n){
	while(k <= n){
		c[k] += value;
		k += lowbit(k);
	}
}

求和每次会得到始点到某个点内的和,所以将更新的过程逆过来,便可以求和

int getSum(int k){
	int ans = 0;
	while(k){
		ans += c[k];
		k -= lowbit(k);
	}
	return ans;
}

理解了之后树状数组很好用的

#include <iostream>
#include <cstdio>
using namespace std;
int c[55];
int lowbit(int k){
	return k&(-k);
}
void build(int jk[], int n){
	int i, k, res, j;
	for(i = 1; i <= n; ++i){
		k = lowbit(i);
		res = 0, j = i;;
		while(k){
			res += jk[j];
			--j, --k;
		}
		c[i] = res;
	}
}
void update(int value, int k, int n){
	while(k <= n){
		c[k] += value;
		k += lowbit(k);
	}
}
int getSum(int k){
	int ans = 0;
	while(k){
		ans += c[k];
		k -= lowbit(k);
	}
	return ans;
}
int main(){
	int i, jk[55] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
	build(jk, 8);
	for(i = 1; i <= 8; ++i)
		printf("%d\n", getSum(i));
	return 0;
}

下面贴一个树状数组应用的代码,求给定数字序列的逆序数个数


#include <algorithm>
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn = 1005;
int a[maxn], c[maxn] = {0}, n;
struct node{
	int value, index, ans;
} jk[maxn];
int cp1(node a, node b){
	return a.value > b.value;
}
int cp2(node a, node b){
	return a.index < b.index;
}
int lowbit(int k){
	return k&(-k);
}
void update(int k, int va){
	while(k <= n){
		c[k] += va;
		k += lowbit(k);
	}
}
int getSum(int k){
	int ans = 0;
	while(k){
		ans += c[k];
		k -= lowbit(k);
	}
	return ans;
}
int main(){
	int i, k, ans;
	cin >> n;
	for(i = 1; i <= n; ++i) {
		cin >> jk[i].value;
		jk[i].index = i;
	}
	sort(jk+1, jk+n+1, cp1);
	ans = 0;
	for(i = 1; i <= n; ++i){
		k = getSum(jk[i].index);
		update(jk[i].index, 1);		//将数从大到小插入c数组中,当然插入的位置是数值原本所在的位置					
		jk[i].ans = k;				//每次插入前求得有多少比当前数值大的数已经插入到当前数组位置的前面去了,便是逆序数
		ans += k;
	}
	sort(jk+1, jk+n+1, cp2);
	for(i = 1; i <= n; ++i){
		printf("%d %d\n", jk[i].value, jk[i].ans);
	}
	printf("%d\n", ans);
	return 0;
}

继续加油~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值