树状数组——点修区查与区修点查

树状数组是一种代码量小,维护区间的数据结构

他可以实现:

1.区间修改,单点查询

2.单点修改,区间查询

当然,二者不可兼得,大人全都要的话,请选择线段树

前置知识:

lowbit(x)操作:

获取x二进制的最后一位1以及其后面的0所组成的数

比如x等于6时,其二进制为110,那么lowbit(6)就等于2,其二进制为10

这里有:

lowbit(x)=x&(-x)

树状数组的特性:

对于树状数组tr[N]而言

tr[x+lowbit(x)]是tr[x]的父亲

对于区间[1,x]而言

其区间和等于tr[x]+\sum tr[x-lowbit(x))] (x>0)



操作:

设原数组为a[N],大小为n,树状数组为tr[N],

1.单点修改,区间查询:

假设我们要对a[x]的权值进行修改,那么我们对tr[x]进行修改,然后不断pushup他的父结点,也就是tr[x+lowbit(x)]就可以了,直到pushup到tr[n]

如果我们要对区间[l,r]进行查询,我们只要求出区间[1,l-1],[1,r],然后利用前缀和就可以求出区间[l,r]的和了

实现代码如下:

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

//单点修改
void pointAdd(int x,int k){
    for (int i = x; i <= n;i+=lowbit(i)){
        tr[i] += k;
    }
}

//查询区间[l,r]
int queryLine(int l,int r){
    int sum = 0;
    for (int i = r; i;i-=lowbit(i)){
        sum += tr[i];
    }
    for (int i = l - 1; i;i-=lowbit(i)){
        sum -= tr[i];
    }
    return sum;
}

2.区间修改,单点查询

其实本质上还是利用树状数组单点修改的方便性

这里我们构造原数组的差分数组d,然后用树状数组来维护数组d

当我们需要对原数组的区间[l,r]进行加权值k的修改时,只需要对差分数组d[l]和d[r+1]进行单点修改就可以了

当我们需要对原数组的a[x]进行查询时,只要求出d数组[1,x]的区间和就可以了

实现代码如下:

//初始化差分数组以及树状数组
void init(){
    for (int i = 1; i <= n;i++){
        d[i] = a[i] - a[i - 1];
        for (int j = i; j <= n;j+=lowbit(j)){
            tr[j] += d[i];
        }
    }
}

//区间修改
void change(int l,int r,int k){
    for (int i = r + 1; i<=n;i+=lowbit(i)){
        tr[i] -= k;
    }
    for (int i = l;i<=n;i+=lowbit(i)){
        tr[i] += k;
    }
}

//单点查询
int query(int x){
    int sum = 0;
    for (int i = x; i;i-=lowbit(i)){
        sum += tr[i];
    }
    return sum;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值