树状数组模版

1.树状数组简介

树状数组是一个很奇特的树,它的节点会比线段树少一些,也能表示一个数组。

树状数组是一种类似于前缀和的数据结构,但是前缀和的修改操作是 O(n) 的,查询是 O(1) 的 。所以就有了树状数组这个数据结构,它的两种操作被中和了,都是 O(logn)的。

线段树也能实现树状数组的功能,但是:相比线段树,树状数组更好写,而且代码很短。

2.基本概念

树状数组是结合了二进制的一种数据结构,它利用二进制来划分每一个节点所表示的前缀和。

树状数组的规律如下:

·C1=a1
·C2=a1+a2
·C3=a3
·C4=a1+a2+a3+a4
·C5=a5
·C6=a5+a6
·C7=a7
·C8=a1+a2+a3+a4+a5+a6+a7+a8

如果这样很难观察的话,把他们改为二进制

·C0001=a0001
·C0010=a0001+a0010
·C0011=a0011
·C0100=a0001+a0010+a0011+a0100

3.单点修改

首先引入一个函数lowbit,lowbit函数可以计算出非负整数n在二进制下最低位1及其后面的0构成的数值,即lowbit(n)=n&(-n).

比如lowbit(5)=lowbit(0101(二进制))=0001(二进制)

而如果改变ax的值,就要加上自己的lowbit,一直加到n。

代码:

void add(int x,int k)
{
    while(x<=n)
    {
        tree[x]+=k;//第x个数加上k
        x+=lowbit(x);
    }
    return;
}
4.区间查询

就是前缀和,比如查询[x,y]的和,那么就将[1,y]的和 - [1,x-1]的和。

从1到y的和求法是,将y转为2进制,然后一直减去lowbit(y),一直到0

int sum(int x)
{
    int ans=0;
    while(x!=0)
    {
        ans+=tree[x];
        x-=lowbit(x);
    }
    return ans;
}
5.区间修改

如果将[x,y]加上一个k,那就是[x,n]都加上一个k,再从[y+1,n]加上一个-k

 void add(int x,int k)
    {
        while(x<=n)
        {
            tree[x]+=k;
            x+=lowbit(x);
        }
    }
6.代码 

树状数组1​​​​​​​

#include<iostream>

using namespace std;

const int N=1e6+10;

int a[N]; 
int n,m;

int lowbit(int x)
{
	return x&-x;
} 

void add(int x,int k)//单点修改 
{
	while(x<=n)
	{
		a[x]+=k;
		x+=lowbit(x);
	}
	return;
}

int sum(int x)
{
	int ans=0;
	while(x!=0)
	{
		ans+=a[x];
		x-=lowbit(x);
	}
	return ans;
}

int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++) 
	{
		int w;
		scanf("%d",&w);
		add(i,w);
	}
	while(m--)
	{
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		if(a==1) add(b,c);
		if(a==2) printf("%d\n",sum(c)-sum(b-1));//[1.c]的和减去[1,b-1]的和 
	}
	return 0;
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值