树状数组
先来看几个问题吧。
1. 什么是树状数组?
顾名思义,就是用数组来模拟树形结构呗。那么衍生出一个问题,为什么不直接
建树?答案是没必要,因为树状数组能处理的问题就没必要建树。和 树的构造
方式有类似之处。
2. 树状数组可以解决什么问题
可以解决大部分基于区间上的更新以及求和问题。
3. 树状数组和线段树的区别在哪里
树状数组可以解决的问题都可以用线段树解决,这两者的区别在哪里呢?树状数
组的系数要少很多,就比如字符串模拟大数可以解决大数问题,也可以解决
的问题,但没人会在 的问题上用大数模拟。
4. 树状数组的优点和缺点
修改和查询的复杂度都是 ,而且相比线段树系数要少很多,比传统数
组要快,而且容易写。
- 缺点是遇到复杂的区间问题还是不能解决,功能还是有限。
先给出一张图
不难得出 :
可以发现,这颗树是有规律的
( 为 的二进制中从最低位到高位连续零的长度 )
那么如何求出二进制中从最低位到高位连续零的长度呢 ?
(我也不懂)
简单来说,树状数组其实其实就是把一个数存在它的二进制中为 的位中。
每次就找一个数的二进制中最后一个 的位置。
例:
的二进制为 。
则 的反码为 。
将 和 进行按位与得,。
的实现
inline int lowbit(int a)
{
return a & (-a);
}
- 单点修改,区间查询
- 模板
- 单点修改
inline void update(int x, int y)
{
while (x <= n)
{
c[x] += y;
x += lowbit(x);
}
}
- 区间查询 要想求出 相当于 减去
inline int query(int x)
{
int ans = 0;
while (x)
{
ans += c[x];
x -= lowbit(x);
}
return ans;
}
故 ,
修改 :
查询 :
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 1e6 + 5;
int n, q, a[maxn], c[maxn];
inline int lowbit(int a)
{
return a & (-a);
}
inline void update(int x, int y)
{
while (x <= n)
{
c[x] += y;
x += lowbit(x);
}
}
inline int query(int x)
{
int ans = 0;
while (x)
{
ans += c[x];
x -= lowbit(x);
}
return ans;
}
signed main()
{
cin >> n >> q;
for (int i = 1; i <= n; i++)
cin >> a[i], update(i, a[i]);
for (int i = 1; i <= q; i++)
{
int x, y, z;
cin >> x >> y >> z;
if (x == 1)
{
update(y, z);
}
else
{
cout << query(z) - query(y - 1) << endl;
}
}
return 0;
}
- 区间修改,单点查询
-
处理
先弄个差分树状数组
update(i, a[i] - a[i - 1]);
-
区间修改
利用前缀和的特性来 更新区间的值。
当区间 加上 时, 与 的差增加了 , 与 的差减少了
inline void update(int x, int y) { while (x <= n) { c[x] += y; x += lowbit(x); } } inline void add(int l, int r, int x) { update(l, x); update(r + 1, -x); }
-
单点查询
inline int query(int x) { int ans = 0; while (x) { ans += c[x]; x -= lowbit(x); } return ans; }
故 ,
修改 :
查询 :
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 1e6 + 5;
int n, q, a[maxn], maxx, c[maxn];
inline int lowbit(int a)
{
return a & (-a);
}
inline void update(int x, int y)
{
while (x <= n)
{
c[x] += y;
x += lowbit(x);
}
}
inline void add(int l, int r, int x)
{
update(l, x);
update(r + 1, -x);
}
inline int query(int x)
{
int ans = 0;
while (x)
{
ans += c[x];
x -= lowbit(x);
}
return ans;
}
signed main()
{
cin >> n >> q;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
update(i, a[i] - a[i - 1]);
}
for (int i = 1; i <= q; i++)
{
int abc, x, y, z;
cin >> abc;
if (abc == 1)
{
cin >> x >> y >> z;
add(x, y, z);
}
else
{
cin >> x;
cout << query(x) << endl;
}
}
return 0;
}
- 区间修改,区间查询
华丽的结束线
不 , 没完
1.单点修改,区间查询
查询时要用到容斥原理
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 5001;
int n, m;
int tree[maxn][maxn];
int lowbit(int x)
{
return x & -x;
}
/////////////////////////////////////
void update(int x, int y, int z)
{
for (int i = x; i <= n; i += lowbit(i))
{
for (int j = y; j <= m; j += lowbit(j))
{
tree[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 += tree[i][j];
}
}
return res;
}
/////////////////////////////////////
signed main()
{
cin >> n >> m;
int op;
while (scanf("%lld", &op) != EOF)
{
int a, b, c, d;
cin >> a >> b >> c;
if (op == 1)
{
update(a, b, c);
}
else
{
cin >> d;
cout << query(c, d) - query(a - 1, d) - query(c, b - 1) + query(a - 1, b - 1) << endl;
}
}
}
- 区间修改,单点查询
这里也需要用到差分 (二维啊!!!)
差分数组
void update(int x, int y, int k)
{
for (int i = x; i <= n; i += lowbit(i))
{
for (int j = y; j <= m; j += lowbit(j))
{
t[i][j] += k;
}
}
}
void range_update(int a, int b, int c, int d, int k)
{
update(a, b, k);
update(a, d + 1, -k);
update(c + 1, b, -k);
update(c + 1, d + 1, k);
}
- 区间修改,区间查询
下面这个式子表示的是点(x, y)的二维前缀和:
时间复杂度
接下来统计一下每个 出现过多少次。 出现了 次,出
现了 次
……
出现了 次。
那么这个式子就可以写成:
把这个式子展开,就得到:
那么我们要开四个树状数组,分别维护:
本文来自博客园,作者:蒟蒻orz,转载请注明原文链接:https://www.cnblogs.com/orzz/p/18122241
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话