树状数组区间更新+区间查询+单点查询

本文详细介绍了如何利用树状数组实现区间更新操作,并通过引入差分数组的概念,实现了单点查询与区间查询的功能。文章提供了具体的代码示例,帮助读者理解树状数组在解决此类问题上的高效应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

为了更好地使用复杂度比线段树更加优化的树状数组,所以必须实现树状数组的区间更新;树状数组时间复杂度为O(MlogN), 实际用的时候优于线段树,且写得少。

 

区间更新、单点查询

引入差分数组,假设初始数据数组为a,另a[0] = 0;设要维护的差分数组为 d[i] = a[i] - a[i-1];进一步可知 a[i] = d[1] + d[2] + ... + d[i]; 即前i项和,为方便记为sigma(d, i);例如如下例子:

a:1 2 3 5 6 9

d:1 1 1 2 1 3

如果进行区间[2, 5]更新加2,则:

a:1 4 5 7 8 9

d:1 3 1 2 1 1

会发现当某个区间[x, y]内值发生了同等改变,但这个区间内的差值还是不变的,只有d[x]和d[y+1]的值发生了改变。

所以可以建立区间更新、单点查询的树状数组去维护d[],例如如下例子:

d:1 1 1 2 1 3

c:1 2 1 5 1 4(c值如何计算参考下图)

区间[2, 5]更新加2,则:

d:1 3 1 2 1 1

c:1 4 1 7 1 2(c值如何计算参考下图)

int lowbit(int k){
	return k & -k;
}

void update(int n, int *c, int i, int va){
	while(i <= n){
		c[i] += va;
		i += lowbit(i);
	}
}

int pointValue(int *c, int i) {
    int res = 0;
    while(i > 0){
        res += c[i];
        i -= lowbit(i);
    }
    return res;
}

//-------------------------------------

// 在区间[x, y]增加k
updata(n, c, x, k);   
updata(n, c, y+1, -k);

// 获得第i点的值
pointValue(c, i);

区间更新、区间查询

根据差分数组:

sum[n]

= a[1] + a[2] + .. + a[n];

sigma(d, 1) + sigma(d, 2) + ... + sigma(d, n);

= n*d[1] + (n-1)*d[2] + ... + 2*d[n-1] + 1*d[n];

n*(d[1] + d[2] +...+ d[n]) - (0*d[1] + 1*d[2] + ... + (n-1)*d[n]);

所以可以得到 sum[n] =n * sigma(d, n)  - (0*d[1] + 1*d[2] + ... + (n-1)*d[n]);

令r[i] = (i-1) * d[i];

则 sum[n] = n * sigma(d, n) - sigma(r, n);

此时则需要构造两个树状数组去分别维护d[]和r[]的更新;例如如下例子:

a:  1 2 3 5 6 9

d:  1 1 1 2 1 3

c1:1 2 1 5 1 4

r:   0 1 2 6 4 15

c2:0 1 2 9 4 19

如果进行区间[2, 5]更新加2,则:

a:  1 4 5 7 8 9

d:  1 3 1 2 1 1

c1:1 4 1 7 1 2

r:   0 3 2 6 4 5

c2:0 3 2 11 4 9

注:c1、c2的计算过程同样参考上图。
注意c2更新前后,其在区间左端点2处开始向后更新加k*(2-1),在右端点5再加1=6处,更新减k*(5-1)。

代码如下:

int lowbit(int k){
	return k & -k;
}

void update(int n, int *c1, int *c2, int i, int va){
    int x = i;
	while(i <= n){
		c1[i] += va;
		c2[i] += va * (x-1);
		i += lowbit(i);
	}
}

int sum(int *c1, int *c2, int i) {
    int res = 0, x = i;
    while(i > 0){
        res += x * c1[i] - c2[i];
        i -= lowbit(i);
    }
    return res;
}

//-------------------------------------

// 在区间[x, y]增加k
updata(n, c, x, k);   
updata(n, c, y+1, -k);

// 获得[x, y]的和
res = sum(c1, c2, y) - sum(c1, c2, x-1);

 

其它细节可参考:https://www.cnblogs.com/xenny/p/9739600.html

### 树状数组实现区间修改与单点查询 树状数组可以用于高效处理 **区间修改** 和 **单点查询** 的问题。通过引入辅助数组 `lz` 来记录延迟更新的信息,可以在 $ O(\log n) $ 时间复杂度下完成这些操作。 以下是具体实现方式: #### 辅助函数定义 为了便于计算,我们需要定义两个核心函数:`lowbit()` 和 `update()` 函数[^3]。 ```cpp int lowbit(int x) { return x & (-x); } ``` 该函数返回参数二进制表示中最右侧的 1 及其后续的所有零组成的数值。 --- #### 区间修改逻辑 对于区间 `[l, r]` 进行法操作(假设增值为 `v`),可以通过如下方式进行分解: - 对位置 `l-1` 执行减去 `v` 的操作。 - 对位置 `r` 执行上 `v` 的操作。 这种差分的思想使得最终只需要对单点执行累即可[^4]。 代码实现如下: ```cpp void add_lazy(int pos, int val) { while (pos <= n) { // 更新树状数组的最大范围 lz[pos] += val; pos += lowbit(pos); } } // 修改区间 [l, r] 中每个元素都上 v void modify_range(int l, int r, int v) { if (l > 1) add_lazy(l - 1, -v); // 差分前缀部分减少 v add_lazy(r, v); // 差分后缀部分增 v } ``` 上述代码中,`add_lazy` 是用来向树状数组中的某个节点及其父节点应用增量的操作。 --- #### 单点查询逻辑 当需要查询某一点的具体值时,我们从当前索引出发向上遍历所有祖先节点,并累积它们所存储的懒标记值。 代码实现如下: ```cpp int query_point(int pos) { int res = a[pos]; // 初始值设为原数组对应位置的值 int idx = pos; while (idx > 0) { res += lz[idx]; idx -= lowbit(idx); } return res; } ``` 这里需要注意的是,在实际编码过程中可能还需要维护原始数组 `a[]` 或者其他形式的基础数据源来初始化结果。 --- ### 完整程序示例 下面给出完整的 C++ 实现代码片段: ```cpp #include <bits/stdc++.h> using namespace std; const int MAXN = 1e5 + 7; long long a[MAXN], lz[MAXN]; int lowbit(int x) { return x & (-x); } void add_lazy(int pos, long long val) { while (pos <= MAXN - 1) { lz[pos] += val; pos += lowbit(pos); } } void modify_range(int l, int r, long long v) { if (l > 1) add_lazy(l - 1, -v); add_lazy(r, v); } long long query_point(int pos) { long long res = a[pos]; int idx = pos; while (idx > 0) { res += lz[idx]; idx -= lowbit(idx); } return res; } int main() { ios::sync_with_stdio(false); cin.tie(0); int n, q; cin >> n >> q; for (int i = 1; i <= n; ++i) cin >> a[i]; while (q--) { char type; cin >> type; if (type == 'M') { // Modify range int l, r, v; cin >> l >> r >> v; modify_range(l, r, v); } else if (type == 'Q') { // Query point int p; cin >> p; cout << query_point(p) << "\n"; } } return 0; } ``` 此代码实现了基于输入命令动态调整区间的功能并支持快速查询任意指定位置的实际数值。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值