树状数组基础

        今天了解了一个叫树状数组的结构,刚学的时候发现,这个和线段树有一点相似的地方,可以更简单的完成单点修改和区间查询等,但是由于目前学的比较基础,对树状数组的了解不多,个人认为树状数组的功能是有限的,线段树能实现一些树状数组所不能实现的功能。(这才对的起线段树长的离谱的代码量)

        简单介绍

        树状数组和二叉树建成的树状结构是有点不太一样的,

        很明显,他不属于二叉树那种严格的两个节点,但是 和二叉树相似的是,都是由节点递归的,那么它是根据什么进行的?

        接下来就来说一下lowbit(x)。

        lowbit是计算一个非负整数n在二进制下的最低为1及其后面的0构成的数.其具体的实现方式是根据一个数的二进制数,及其补码。lowbit(x) = x&(-x)。

        我们通过观察节点的二进制数,进一步发现,树状数组中节点x的父节点为x+lowbit(x),例如t[2]的父节点为t[4]=t[2+lowbit(2)]

例题https://www.luogu.com.cn/problem/P3374

        本题大意 : 单点修改区间查询。

        在修改的时候,我们需要根据lowbit函数,在修改一个的时候,对所有的节点进行更新。

        查询的时候,我们需要根据前缀和的原理,比如查询2 到 4的值。我们需要算1到4的值,减去1到2的值即可。

        Code :

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#define ll long long 
#define buff ios::sync_with_stdio(false)
using namespace std;
const int N = 1e6 + 10;
ll a[N],b[N],c[N];
ll n, m ,x;
ll op,y;
int lowbit(int x)
{
	return x & (-x);
}
void modify(int x,int k) //修改 
{
	for(int i = x; i<= n ; i += lowbit(i)) // 更新所有父节点。 
	{
		a[i] += k;
	}
}
int query(int l,int r) //查询 
{
	ll ans = 0;
	for(int i = r; i >=1; i -= lowbit(i)) // 计算1 ~ r 
	{
		ans += a[i]; // 相加 
	}
	for(int i = l - 1; i >= 1 ; i -= lowbit(i))//计算1 ~l - 1 
	{
		ans -= a[i]; // 减去 
	}
	return ans;
}
int main()
{
	buff; 
	cin >> n >> m;
	for(int i = 1; i <= n; i ++)
	{
		cin >> x;
		modify(i,x);
	}
	while(m--)
	{
		cin >> op >> x >> y;
		if(op == 1)
		{
			modify(x,y);
		}
		else
		{
			ll ans = query(x,y);
			cout << ans << endl;
		}
	}
}

         区间修改,单点查询

       题目链接  :   https://www.luogu.com.cn/problem/P3368

       题目大意 :修改一个区间的值,进行单点查询。

        这里和前面不一样的是,在修改的时候,我们需要一个差分数组进行维护,在查询的时候,根据差分数组进行前缀求和即可。

        code :

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#define ll long long 
#define buff ios::sync_with_stdio(false)
using namespace std;
const int N = 1e6 + 10;
ll a[N],b[N],c[N];
ll n, m ,x ,z ;
ll op,y;
int lowbit(int x)
{
	return x & (-x);
}
void modify(int x,int k)
{
	for(int i = x; i<= n ; i += lowbit(i))
	{
		c[i] += k;
	}
}
int query(int l,int r) 
{
	ll ans = 0;
	for(int i = r; i >= l ; i -= lowbit(i)) // 差分数组求和 
	{
		ans += c[i];
	}
	return ans;
}
int main()
{
	buff; 
	cin >> n >> m;
	for(int i = 1; i <= n; i ++)
	{
		cin >> a[i];
		b[i] = a[i] - a[i - 1]; // 差分数组 
		modify(i,b[i]); //构造树状数组。 
	}
	while(m--)
	{
		cin >> op;
		if(op == 1)
		{
			cin >> x >> y >> z;
			modify(x,z); // 差分性质进行维护 
			modify(y + 1, -z);
		}
		else
		{
			cin >> x;
			ll ans = query(1,x);
			cout << ans << endl;
		}
	}
}

         结束。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值