算法学习笔记:树状数组/线段树

树状数组

  1. 支持单点修改和区间查询两个操作,单次操作O(logn)
  2. tr[x]数组存的是原数组在区间 ( x-lowbit(x), x] 上的和
  3. 若要将一个数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;
}

线段树

  1. 区间[L, R]划分为[L, Mid]和[Mid+1, R], Mid = L+R2
  2. 单点修改和区间查询单次操作O(logn)
  3. 单点修改:
  4. 区间查询:只返回[L, R]完全包含u区间的值,若没有完全包含该区间,则继续递归
  5. 下标为x的点,父节点为x>>1,左儿子为x<<1,右儿子为x<<1|1
  6. 下标从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);
    }
}
posted @   esico  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示