树状数组入门(处理区间的动态变化)

本文介绍了树状数组(也称 BIT,Binary Indexed Tree)在处理单点更新和区间查询问题上的应用。通过示例代码展示了如何使用树状数组实现对单个元素的增减操作,并快速获取区间元素之和。同时,还解释了如何进行区间更新并进行单点查询。这两个问题在动态维护数组和求和问题中十分常见。

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

1.单点更新,区间查询

更新的是每一个点,求的是区间和

原地址HDU1166

敌兵布阵 - HDU 1166 - Virtual Judge

#include <bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
#define ts                       \
    ios::sync_with_stdio(false); \
    cin.tie(0);
#define endl '\n'
#define pb push_back
typedef long long ll;
int a[50005], c[50005], n; //原始数组 树状数组
int lowbit(int x)        //树状数组最核心功能
{
    //方法一
    return x - (x & (x - 1));
    //方法二
    return x & -x;
}
int getsum(int x) //求区间前x项的和
{                 //时间复杂度O(log n)
    int sum = 0;
    while (x)
    {
        sum += c[x];
        x -= lowbit(x);
    }
    return sum;
}
void update(int x, int v, int flag) //单点更新 v表示变化量
{
    if (flag == 1) //表示增加人数
    {
        while (x <= n)
        {
            c[x] += v;
            x += lowbit(x); //包含x的营地
        }
    }
    else //减少人数
    {
        while (x <= n)
        {
            c[x] -= v;
            x += lowbit(x);
        }
    }
}
int main()
{
    ts;
    int t;
    cin >> t;
    for (int tot = 1; tot <= t; tot++)
    {
        memset(a,0,sizeof a);
        memset(c,0,sizeof c);
        cout << "Case " << tot << ':' << endl;
        cin >> n;
        for (int i = 1; i <= n; i++)
        {
            cin >> a[i];
            update(i, a[i], 1);
        }
        string s;
        while (cin >> s)
        {
            int a, b;
            if (s == "Query")
            {
                cin >> a >> b;
                cout << getsum(b) - getsum(a - 1) << endl; //求区间的总人数
            }
            if (s == "Add") //增加人数
            {
                cin >> a >> b;
                update(a, b, 1);
            }
            if (s == "Sub") //减少人数
            {
                cin >> a >> b;
                update(a, b, 2);
            }
            if (s == "End")
                break;
        }
    }
    return 0;
}

2.区间更新,单点查询

更新的是区间,求的是单点

原数组为a,树状数组为c,其中c[i]=a[i]-a[i-1],则可以通过求树状数组c的前缀和来实现单点查询a[i].

区间修改

当给区间[l,r]修改x的时候,a[l]与a[l-1]的差值增加了x,a[r+1]与a[r]的差值减少了x。我们只需要对树状数组c[l]加上x和对c[r+1]减去x即可。

1 int n,m;
 2 int a[50005] = {0},c[50005]; //对应原数组和树状数组
 3 
 4 int lowbit(int x){
 5     return x&(-x);
 6 }
 7 
 8 void updata(int i,int k){    //在i位置加上k
 9     while(i <= n){
10         c[i] += k;
11         i += lowbit(i);
12     }
13 }
14 
15 int getsum(int i){        //求D[1 - i]的和,即A[i]值
16     int res = 0;
17     while(i > 0){
18         res += c[i];
19         i -= lowbit(i);
20     }
21     return res;
22 }
23 
24 int main(){
25     cin>>n;27     for(int i = 1; i <= n; i++){
28         cin>>a[i];
29         updata(i,a[i] - a[i-1]);   //输入初值的时候,也相当于更新了值
31     }
32     
33     //[x,y]区间内加上k
34     updata(x,k);    //A[x] - A[x-1]增加k
35     updata(y+1,-k);        //A[y+1] - A[y]减少k
36     
37     //查询i位置的值
38     int sum = getsum(i);
39 
40     return 0;
41 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值