个人学习笔记平台,欢迎大家交流

红叶~

Be humble, communicate clearly, and respect others.

树状数组与线段树

树状数组

树状数组:O(logn)

操作:1、给某个位置上的数加上一个数 (单点修改)

2、求某一个前缀和 (区间查询)

支持修改和查询同时进行,是在线的

image-20220305103945365

树状数组下标 所在的层次:x的二进制最后有几个0

C[x]表示的数的范围(x2k,x]=(xlowbit(x),x]

6=(0110)2, 末尾有1个0, 则C[6]表示的和为(4,6]

求前缀和,递推把包括的子区间的和都加起来

// 前 x 个数的和
int query(int x) {
int res = 0;
for(int i = x; i > 0; i -= lowbit(i))
res += C[i];
}

在某个位置上加上某个数v

// 更新x位置上连接的父节点数组C的的值
void add(int x, int v) {
for(int i = x; i <= n; i+= lowbit(i))
C[i] += v;
}

当初始化原数组时候,直接在某个位置上加上原数组位上对应的值。

for(int i = 1;i <= n; i++) {
int x;
cin >> x;
add(i, x);
}

线段树

image-20220306095111238

操作

1、单点修改 O(logn)

2、区间查询 O(logn)

3、区间修改 --> 懒标记(暂时不学)

方法

0、线段树节点

struct Node {
int l, r;
int sum;
}tr[N * 4];

1、pushup: 用子节点信息更新当前节点信息

void pushup(int u) {
tr[u].sum = tr[u << 1].sum + tr[u << 1 + 1].sum;
}

2、bulid: 在一段区间上初始化线段树

void bulid(int u, int l, int r) {
if(l == r) tr[u].sum = {l, r, w[r]};
else {
tru[u] = {l, r};
int mid = l + r >> 1;
bulid(u << 1 , l, mid), bulid(u << 1 + 1, mid+1, r);
pushup(u); // 合并两个子树到根节点
}
}

3、modify: 修改

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); // 用左右子树更新根节点
}
}

4、query: 查询

int query(int u, int l, int r) {
if(tr[u].l >= l && tr[u].l <= 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);
}

注意:查询的时候左右子树的区间都取[l,r]的原因是,由于mid的原因,可能最后会多算。比如查询区间25 的和,递归到[5,7]时要查询的区间为[5,5]。那么到了[5,6]要求的就变成了[5,6],这时候就多算了一个6号节点。

posted @   红叶~  阅读(34)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示