树状数组区间更新

#include<stdio.h>
#include<iostream>
#include<string.h>
#define maxn 101000
#define mem(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x&(-x)
#define sf scanf
#define pf printf
using namespace std;

int n,q,num;
int c[maxn];

void update(int i,int x){
    for(;i<=n;i+=lowbit(i)) c[i]+=x;
}

int sum(int a){
    int s=0;
    for(int i=a;i>0;i-=lowbit(i)) s+=c[i];
    return s;
}

int main(){
    freopen("a.txt","r",stdin);
    sf("%d",&n);
    for(int i=1;i<=n;i++){
        int x;
        scanf("%d",&x);
        update(i,x);
    }
    scanf("%d",&q);
    while(q--){
        scanf("%d",&num);
        if(num==1){
            int a,b,c;
            sf("%d%d%d",&a,&b,&c);
            for(int i=a;i<=b;i++) update(i,c);//区间更新
        }
        else{
            int a;
            sf("%d",&a);
            pf("%d\n",sum(a)-sum(a-1));
        }
    }
    return 0;
}

### 树状数组区间修改的实现方法 树状数组是一种高效的数据结构,用于处理动态前缀和问题。通过引入差分数组的概念,可以将其扩展到支持区间修改的操作。 #### 差分数组的基础概念 为了实现区间修改,通常会利用差分数组的思想。假设有一个初始数组 `a`,定义其对应的差分数组 `delta` 为: \[ \text{delta}[i] = a[i] - a[i-1] \] 对于任意一段区间的加法操作 `[L, R]` 加上某个值 `val`,可以通过更新差分数组来完成: - 将 `\text{delta}[L]` 增加 `val` - 将 `\text{delta}[R+1]` 减少 `val` 这样,在最终计算原数组时,只需对差分数组求前缀和即可恢复原始数据[^3]。 #### 使用两个树状数组辅助实现 由于直接维护整个差分数组可能效率较低,因此采用两棵树状数组分别存储不同的信息: 1. **第一个树状数组 (`c1`):** 维护差分数组本身。 2. **第二个树状数组 (`c2`):** 维护位置乘以差分数组的结果,即 \(x \cdot \text{delta}[x]\)。 这种设计使得我们可以快速执行区间修改以及查询指定范围内的总和。 以下是基于此原理的一个具体实现: ```cpp #include <bits/stdc++.h> using namespace std; typedef long long ll; const int MAXN = 2e5 + 5; ll c1[MAXN], c2[MAXN]; int n; // 计算 lowbit 的函数 inline ll lowbit(ll x) { return x & (-x); } // 更新树状数组 void update(ll *tree, int idx, ll delta) { while (idx <= n) { tree[idx] += delta; idx += lowbit(idx); } } // 查询树状数组中的前缀和 ll query(ll *tree, int idx) { ll res = 0; while (idx > 0) { res += tree[idx]; idx -= lowbit(idx); } return res; } // 初始化树状数组 void init(vector<ll> &arr) { for (int i = 1; i <= n; ++i) { ll diff = arr[i] - arr[i - 1]; update(c1, i, diff); update(c2, i, (ll)(i - 1) * diff); } } // 执行区间 [l, r] 上增加 val 的操作 void range_add(int l, int r, ll val) { update(c1, l, val); update(c1, r + 1, -val); update(c2, l, (ll)(l - 1) * val); update(c2, r + 1, -(ll)r * val); } // 查询区间 [l, r] 的和 ll range_sum(int l, int r) { ll sum_r = (r + 1) * query(c1, r) - query(c2, r); ll sum_l_minus_1 = l * query(c1, l - 1) - query(c2, l - 1); return sum_r - sum_l_minus_1; } int main() { ios::sync_with_stdio(false); cin.tie(0); vector<ll> arr(MAXN, 0); cin >> n; for (int i = 1; i <= n; ++i) cin >> arr[i]; init(arr); int q; cin >> q; while (q--) { char op; cin >> op; if (op == 'U') { // Update operation int l, r; ll val; cin >> l >> r >> val; range_add(l, r, val); } else if (op == 'Q') { // Query operation int l, r; cin >> l >> r; cout << range_sum(l, r) << "\n"; } } return 0; } ``` 这段代码展示了如何使用双树状数组来进行高效的区间修改与查询操作。它先初始化输入序列并构建相应的树状数组;随后按照指令逐一处理用户的请求——无论是更改还是询问特定子段之累加值得情况都会被妥善解决[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值