前言:
这理论上是一篇复习的笔记,不会讲的很细,待到补测完再来完善。
概述&定义
先用一张图表示树状数组:

我们定义 ai 表示数组元素,ci 表示 [ai−2x+1,ai] 之间数的总和,其中 x 表示为 i 在二进制下末尾 0 的个数。
比如,6 在二进制下表示为 110,c6 表示 [a6−21+1,a6] 之间的和。
实现
- 显然的,对于一个 i,我们要求它二进制下末尾 0 的个数,使用 lowbit 函数。
int lowbit(int x)
{
return x&(-x);
}
通过这个函数,我们就可以求出 x 的上下级,即 x+lowbit(x) 和 x−lowbit(x)。
观察上面的那个图,很容易想到,如果我要修改 x 的值,那么对应的,他的上级的 ci 的值也会改变,而刚才我们讲过求 x 的上下级,所以直接对他的上级进行修改即可。
void update(int x,int val)
{
for(int i=x;i<=n;i+=lowbit(i))
c[i]+=val;
}
- 单点修改单点查询
对于一个点,在树状数组中,我们仅可以查询他的前缀和,如果我要查询 ai 的前缀和,那就是不断查询 cx−lowit(x) 的值并且相加。
int ask(int x)
{
int sum=0;
for(int i=x;i>0;i-=lowbit(i))
sum+=c[i];
return sum;
}
先考虑一个弱化问题,区间修改单点查询。
这可以维护一个 d (差分数组)。
而区间修改就体现在差分上,假如对 [l,r] 加上 x,那就是在 l 上加上 x,在 r+1 上减去 x,再跑一次前缀和就可以得到 i 这个位置的数值。
先考虑如何区间查询,用 s1(x) 表示 ∑i=1xdi
假设我们要查询 [l,r] 中 ai 的和,显然是:
i=l∑rai
先考虑预处理出差分数组 d。
i=l∑rj=1∑idj
(r−l+1)i=1∑l−1di+i=l∑r(r−i+1)×di
(r−l+1)s1(l−1)+(r+1)i=l∑rdi−i=l∑rdi×i
(r−l+1)s1(l−1)+(r+1)+(r+1)(s1(r)−s1(l−1))−i=l∑rdi×i
(r+1)×s1(r)−l×s1(l−1)−i=l∑rdi×i
用 s2(x) 表示 ∑i=lrdi×i。
所以最后就是:
(r+1)×s1(r)−l×s1(l−1)−s2(r)+s2(l−1)
对于 s1 这个BIT,显然可以对 l 加 x,对 r+1 减去 x 即可。
对于 s2 来说,就是对于 l 加 x×l,对于 r+1 减去 (r+1)×x (感性理解下,因为要维护的是 di×i 因为是单点修改,所以如上即可。)
用处不大,静态不如 ST 表好写,动态不如线段树,不讲。
对于逆序对,我们考虑 ai>aj,并且 i<j 称之为逆序对。
我们不妨开一个值域大小的树状数组,对于当前的数来说,我们对他单点修改加上 1,表示这个数出现了一次。
假设当前的数为 aj,1≤i≤j。首先对于所有的 ai 来说,必然有 i<j,我们再统计树状数组中 j 的前缀和,也就是表示说在 aj 之前出现过的,且比他要小的数的出现次数,那么这一些数势必是 ai<aj,且 i<j 的。那么对于前 j 个数来说,统计出了比 aj 小的,剩下的势必就是满足 i<j,ai>aj的了,那以 aj 为结尾的逆序对数量就是 j−sumj。
当然,开一个值域范围的树状数组可能会有一点问题,就是当值域太大时,我们需要离散化。
假设有两组数 1,2,3 和 1,2,1000,其实对于以 1000 为末尾的逆序对和以 3 为末尾的逆序对是一样的。所以我们通过离散化来表示数与数之间的相对大小关系即可。
具体做法就是先对这个数组进行排序,然后赋值他相对的排名即可。
和逆序对是如出一辙的。
首先你要知道有一个 n2 的 dp。
然后我们在这个 dp 上要枚举前面的数,如果比他小就更新现在的值。
所以我们现在考虑优化这个过程。
我们开一个值域大小的树状数组,表示在 ai 小的数字出现次数为结尾的最长不下降子序列的长度。注意是边遍历一边更新,不能预处理,原因是目的要找 [1,i] 中出现的,不然如果在 [i+1,n] 中有 [1,ai] 的数就寄了。进来一个数就是单点修改,然后用 log(n) 的时间找到最大值加一,单点修改即可。
upd in :2023.2.6 大力修改了一下树状数组区间修改,加上树状数组优化最长不下降子序列。
upd in:2023.2.7 修改了一点错误。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现