算法学习笔记:树状数组/线段树
树状数组
- 支持单点修改和区间查询两个操作,单次操作O(logn)
- tr[x]数组存的是原数组在区间 ( x-lowbit(x), x] 上的和
- 若要将一个数x变为v,则将x+(-x)+v,即加上v-x
模板如下:
点击查看代码
// 建立树状数组
for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
for (int i = 1; i <= n; i ++ ) add(i, a[i]);
//lowbit操作
int lowbit(int x)
{
return x & -x;
}
//单点修改,x为下标,v为要加的数
void add(int x, int v)
{
for (int i = x; i <= n; i += lowbit(i)) tr[i] += v;
}
//区间查询,查询结果为原数组区间[1, x]的和,x为下标
int query(int x)
{
int res = 0;
for (int i = x; i; i -= lowbit(i)) res += tr[i];
return res;
}
线段树
- 区间[L, R]划分为[L, Mid]和[Mid+1, R], Mid =
- 单点修改和区间查询单次操作O(logn)
- 单点修改:
- 区间查询:只返回[L, R]完全包含u区间的值,若没有完全包含该区间,则继续递归
- 下标为x的点,父节点为x>>1,左儿子为x<<1,右儿子为x<<1|1
- 下标从1开始
模板如下:
点击查看代码
// 结构体定义线段区间,最多为4n个
struct Node
{
int l, r;
int sum;
}tr[N * 4];
// 用子节点信息更新当前节点信息,可能会被省略到程序内部去
void pushup(int u)
{
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
//在一段区间上初始化线段树
void build(int u, int l, int r)
{
if (l == r) tr[u] = {l, r, w[r]};
else
{
tr[u] = {l, r};
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
}
//查询区间[L,R]的和
int query(int u, int l, int r)
{
if (tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
int mid = tr[u].l + tr[u].r >> 1;
int sum = 0;
if (l <= mid) sum = query(u << 1, l, r);
if (r > mid) sum += query(u << 1 | 1, l, r);
return sum;
}
//修改,x为要修改点的下标
void modify(int u, int x, int v)
{
if (tr[u].l == tr[u].r) tr[u].sum += v;
else
{
int mid = tr[u].l + tr[u].r >> 1;
if (x <= mid) modify(u << 1, x, v);
else modify(u << 1 | 1, x, v);
pushup(u);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人