P3374 【模板】树状数组 1

22 阅读3分钟

P3374 【模板】树状数组 1

树状数组解决的是一类前缀和与差分的问题,这道题目如果使用差分与前缀和的话将会面临一个问题,就是每一次操作2之前都需要刷新一遍前缀和数组,会导致tle。

所以就需要引入树状数组,进行单点修改+单点查询。

所谓的树状数组,形象地看是一张倾斜的树,如下

(从题解区搬得图片)

通过这张图可以看出来树状数组其实就是每一个数组会管理相应的一堆基础数组,如此一来便可只修改一个点。

1.如何分配数组的归属(也就是如何分配上图中c数组与a数组之间的从属关系)


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

以上代码的作用是返回一个数字二进制中由低位到高位出现的第一个‘1’所代表的十进制数,举个例子:

lowbit(7) = lowbit(111(二进制)) = 001(二进制) = 1

lowbit(14) = lowbit(1110(二进制)) = 0010(二进制) = 2

用以上办法分组可以保证每一个c数组管辖的a数组都是连续不遗漏的。


2.如何将底层数组传入他所属的高层数组

关于lowbit有两个规律,如下:

1.每一个数的lowbit刚好是该数字所能覆盖的数的个数。

2.每一个数 + 该数的lowbit值都是其被覆盖的第一个数的值。

知道了lowbit以及上述两条规则,我们就可以有以下代码来将a数组分配到对应的c数组,代码如下:


void add(int u,int cnt)
{
	for(int i = u ; i <= n ; i += lowbit(i))
	{
		c[i] += cnt;
	}
}

以上代码就可以将a数组的值不断地从树的最底层一层一层的往上送入n以内所能到达的最顶层的c数组,数组的初始化以及对数组单个值的修改都可以通过这个函数来实现。


3.如何取出底层数组的前n项和

先贴代码


int query(int k)
{
	int ans = 0;
	for(int i = k ; i >= 1 ; i -= lowbit(i))
	{
		ans += c[i];
	}
	
	return ans;
}

这个代码是求和k及其以下所能到达的所有点的元素和,也就是底层数组的前n项和。

这个query的代码很像add代码,它的意义是从树顶往下吸纳,如果到了底层并且不是1说明它的lowbit值是1,减去1后便又到了左边的顶层继续往下吸纳。

求和代码如下


int x,y;
cin>>x>>y;
			
cout<<query(y) - query(x - 1)<<"\n";

这一行代码有点像前缀和,但也确实是和前缀和一个思路,就不过多赘述了,如果还想不通可以结合上面的图来一步步代入来找规则。最后是这道题的AC代码

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair <ll , ll> pii;
typedef priority_queue <ll ,vector<ll> ,greater<ll> > xiao;  
typedef priority_queue <ll ,vector<ll> ,less<ll> > da; 
const int N = 5e5 + 10,M = 0x3f3f3f3f;
const ull P = 131;

int n,m;
int c[N];

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

void add(int u,int cnt)
{
	for(int i = u ; i <= n ; i += lowbit(i))
	{
		c[i] += cnt;
	}
}

int query(int k)
{
	int ans = 0;
	for(int i = k ; i >= 1 ; i -= lowbit(i))
	{
		ans += c[i];
	}
	
	return ans;
}

int main()
{
	std::ios::sync_with_stdio(false);
    std::cin.tie(0),cout.tie(0);
    
    cin>>n>>m;
	
	for(int i = 1 ; i <= n ; i++)
	{
		int num;
		cin>>num;
		add(i , num);
	} 
	
	while(m--)
	{
		int flag;
		cin>>flag;
		if(flag == 1)
		{
			int x,y;
			cin>>x>>y;
			add(x , y);
		}else
		{
			int x,y;
			cin>>x>>y;
			cout<<query(y) - query(x - 1)<<"\n";
		}
	}
}