树状数组

前言

本蒟蒻学习了7,8遍树状数组才学会,原因不是因为我笨,而是没有把根本的理念先搞懂,so,我打算写这篇博客,把树状数组根本剖析一下

用途

用于快速单点修改,快速查询前缀和,从而快速查询区间和

工作原理


其中有数字的点存储的是树状数组的 tr[] ,灰色节点则是为了方便理解而画出的子虚乌有的节点,一个节点上的tr值就是它下面所直连的子节点的和,为什么要这样设计呢,手模一下可以发现一个tr节点的高度就是它数字转化为二进制第一个1出现的位置,
再进一步手模发现,一个tr节点下面数字的和就是这个数字去掉最后一个1在加1到这个数字的中的所有 ai,抱歉这里表述的不太明白,还是看例子吧

修改,维护前缀和,只需要在最后的1加上1就可以了

lowbit(x)

lowbit(x)=x&x 功能是找到x的最后一个1

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5;
int n,q,op,l,r;
int a[N],tr[N];
int lowbit(int x){
	return x&(-x);
}
void add(int x,int y){
	for(;x<=n;x+=lowbit(x)){
		tr[x]+=y;
	}
}
int query(int x){
	int res=0;
	for(;x;x-=lowbit(x)){
		res+=tr[x];
	}
	return res;
}
signed main(){
	scanf("%lld%lld",&n,&q);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
		add(i,a[i]);
	}
	for(int i=1;i<=q;i++){
		scanf("%lld%lld%lld",&op,&l,&r);
		if(op==1)  add(l,r);
		else  printf("%lld\n",query(r)-query(l-1));
	}
}

一本通oj例题

T2

我们注意到逆序对有两维的要求,我么们可以通过枚举一维顺序,再用树状数组维护所有在i前比它大的数的个数,就可以了

T3

对于这一类的题,先考虑dp,我们设 dp[i][j] 表示以第i个数为结尾,长度为j的严格上升子序列的个数

然后dp转移方程式就是 dp[i][j]=a[k]<a[i],k<ikdp[k][j1]

考虑因为有 a[k]<a[i],k<i 的限制并且还有加和,所以用树状数组优化,对于每一个j开一维树状数组维护前缀和即可

注:答案要取模,我以后再也不犯这样傻逼的错误了

T4:

推式子

T5:

考虑一种思想,在一维的树状树组的每一个位置上分别再开一维树状数组,看代码可能就秒懂力(

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=(1<<12)+50;
int n,m,op,a,b,c,d;
int tr[N][N];
int lowbit(int x){
	return x&(-x);
}
void add(int x,int y,int z){
	for(int i=x;i<=n;i+=lowbit(i)){
		for(int j=y;j<=m;j+=lowbit(j)){
			tr[i][j]+=z;
		}
	}
}
int query(int x,int y){
	int res=0;
	for(int i=x;i;i-=lowbit(i)){
		for(int j=y;j;j-=lowbit(j)){
			res+=tr[i][j];
		}
	}
	return res;
}
signed main(){
	scanf("%lld%lld",&n,&m);
	while(scanf("%lld",&op)!=EOF){
		if(op==1){
			scanf("%lld%lld%lld",&a,&b,&c);
			add(a,b,c);
		}
		else{
			scanf("%lld%lld%lld%lld",&a,&b,&c,&d);
			printf("%lld\n",query(c,d)+query(a-1,b-1)-query(c,b-1)-query(a-1,d));
		}
	}
}

T6:

和区间修区间查一样的套路

维护4个树状数组即可

T7:

先考虑二维树状数组,发现好像空间会超不好办

注意到题目给的条件

不会有星星重叠。星星按 y 坐标增序给出,y 坐标相同的按 x 坐标增序给出。

我们考虑固定一维y升序考虑,出题人真良心,都不用自己排序了,然后只需要树状数组维护所有x小于当前点的x的个数就行了

T8:

傻逼ybt,它没写i<j的条件,害的我迷惑了半天

式子转化成 a[i]i<a[j]j 然后直接做就做完了,注意要离散化一下

T9:

非常巧妙的题,使我的脑袋旋转

首先我们每次只能选最大的值删除,所以删除顺序是固定的

考虑将一些元素从一个堆弹到另一个堆,会使这些元素顺序反过来

然后于是我们就将两个栈顶拼在一起,记录栈顶位置,然后每次在栈中寻找最大的元素,树状数组维护栈顶距离最大值中间有多少个元素即可

T10:

做过类似的题,还写过题解

解释起来有点复杂,我当时题解写了好久,算了,懒得写了

T11:

x=0 时,
将每一项 ai 拆分成二进制,记录每一位(即aimod2i)等于1的数有多少个,
如果该位如果 y 的第 2i 位也正好等于1,那么将 ans 加上 fi2i

x0 时,
还是一位一位增加答案,假设现考虑第i位,则显然是[2i,2i+11]的数是可行的,
所以我们可以建立树状数组ci,j来记录每项 amod2i+1,但还是要对x进行讨论:
姑且将 xmod2i+1 先。

x2i时 ,
ans 加在 [2ix,2i+11x]之间的数的个数*2i

x>2i时 ,
ans 加在 [0,2i+11x][2i+1+2ix,2i+11]之间的数的个数*2i

所以开20棵树状数组,分别维护序列模上 2i 后加入树状数组,然后数点即可

posted @   daydreamer_zcxnb  阅读(26)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
点击右上角即可分享
微信分享提示