树状数组模板

对于一个普通数组,时间复杂度为单点修改O(1),区间求和O(n);
可对其预处理前缀和,则时间复杂度为区间求和O(1),但单点修改变为O(n)。

已知一个算法的时间复杂度取决于最大的那一步,所以为了均衡,便可用树状数组【时间复杂度O( log(n) )】处理该问题。


下面是树状数组的几个模板:

①单点修改,区间查询。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e5 + 5;
int n, m;
struct BIT {
    int c[maxn];
    int lowbit(int x) {return x & (-x);} //求数组下标二进制的非0最低位所表示的值
    void add(int i, int val) { //单点更新,第i个数加上val
        while (i <= n) {
            c[i] += val;
            i += lowbit(i); //向上更新树状数组C,从左往右更新
        }
    }
    int query_sum(int i) { //求区间[1,i]内所有元素的和
        int ret = 0;
        while (i > 0) {
            ret += c[i];
            i -= lowbit(i);
        }
        return ret;
    }
}tr;
int main()
{
    scanf("%d%d", &n, &m); //n个数,m个操作
    int a;
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a);
        tr.add(i, a);
    }
    int t, x, y; 
    while(m--) {
        scanf("%d%d%d", &t, &x, &y); //t为1时第x个数加上y,t为2时求[x,y]的区间和
        if (t == 1) tr.add(x,y);
        else printf("%d\n", tr.query_sum(y) - tr.query_sum(x-1));
    }
    return 0;
}
②区间修改,单点查询。

差分思想,将原数组a差分得到数组b (b[i] = a[i] - a[i-1]),单点查询 a[i] 即变为求差分数组b的前缀和 sum[i] 的区间求和问题,区间修改 a[l, r] 则变为对 b[l] + 1, b[r+1] - 1 的单点修改问题。如此便将原数组a的区间修改,单点查询问题转化为了对其差分数组b的单点修改,区间查询问题了。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e5 + 5;
int n, m;
struct BIT {
    int c[maxn];
    int lowbit(int x) {return x & (-x);}
    void add(int i, int val) {
        while (i <= n) {
            c[i] += val;
            i += lowbit(i);
        }
    }
    void modify(int l, int r, int val) { //区间修改,[l, r]区间所有的数都+val
        add(l, val), add(r+1, -val);
    }
    int query(int i) { //单点查询,查询第i个数
        int ret = 0;
        while (i > 0) {
            ret += c[i];
            i -= lowbit(i);
        }
        return ret;
    }
}tr;
int main()
{
    scanf("%d%d", &n, &m);  //n个数,m个操作
    int a, pre = 0;
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a);
        tr.add(i, a - pre); //差分
        pre = a;
    }
    int t, x, y, k;
    while(m--) {
        scanf("%d", &t);
        if (t == 1) { //t为1时将区间[x,y]内每个数加上k
            scanf("%d%d%d", &x, &y, &k);
            tr.modify(x, y, k);
        }
        else { //t为2时查询第x个数的值
            scanf("%d", &x);
            printf("%d\n", tr.query(x));
        }
    }
    return 0;
}

https://www.luogu.com.cn/blog/Chanis/super-BIT

http://www.cppblog.com/menjitianya/archive/2015/11/02/212171.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值