树状数组初步体会

  •  

    树状数组初步体会

学习来自https://www.bilibili.com/video/av18735440?from=search&seid=12510069409139918776电子科技大学算法讲堂

https://www.cnblogs.com/RabbitHu/p/BIT.html大佬的文章


这里d数组是我的树状数组,a数组就是初始数组了,让你修改查询的

这就是树状数组的存储方式,不难看出它的存储是和二进制有关的(滑稽,好看出个鬼)举个栗子d[6]=a[5]+a[6]; (6的二进制数是110,末尾一个0,那d[6]就是存储2^1个元素的和) 同理我们也可以验证d[8]=a[1]+...a[8](因为8的二进制数是1000,有3个零,就是存储2^3个)  ; 而查询前缀和,就需要用到lowbit这个函数,非常巧妙,我说不太清,看视频说的非常清楚。

int lowbit(int x){
	return x&(-x);
} 
x-=lowbit(x);
or x+=lowbit(x);

也可以直接使用 x+=x&(-x) or x-=x&(-x)

比如查询13这个位置的前缀和 13的二进制数为1101,我们将其拆分为(lowbit就是在此起着至关重要的作用)

1101                    d[13]               a[13]                  //从后往前以1为末尾,后面全是0

1100                    d[12]               a[9]+a[12]

1000                    d[8]                 a[1]+...a[8]

所以前13的前缀和就是 d[13] +d[12] +d[8] 

而修改和查询的顺序是反的。

扫描二维码关注公众号,回复: 2325591 查看本文章

这里我搬运了大佬的代码(笑嘻嘻)

单点修改+区间查询 

//单点修改+区间查询 
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn=1e7+5;
ll sum[maxn],n;//数组从1开始到n 
void add(int p, int x){ //给位置p增加x
    while(p <= n) sum[p] += x, p += p & -p;
}
ll ask_len(int p){ //求位置p的前缀和
    ll res = 0;
    while(p) res += sum[p], p -= p & -p;
    return res;
}
ll range_ask(int l, int r){ //区间求和
    return ask(r) - ask(l - 1);
} 
int main(){
	return 0;
}

而区间修改的话,其实就是用了一个差分的思想了;

比如 a数组         1  1  1  1  1         

        b数组         1  0  0  0  0        b[n]=a[n]-a[n-1]

我想要再a数组的2~3都加上2;

       a数组          1  3  3  3  1

       b数组          1  2  0  0  -2    我们发现只要修改b数组两个元素就可以来维护a数组了

a数组就是b数组的前缀和

所以区间修改 只要修改俩个元素就行了(舒舒服服)。

区间修改+单点查询

(这里都搬运的大佬的详解)

//区间修改+单点查询 
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn=1e7+5;//这里可以理解成 原数组是sum数组的前缀和 
ll sum[maxn],n;//数组从1开始到n 
void add(int p, int x){ //这个函数用来在树状数组中直接修改
    while(p <= n) sum[p] += x, p += p & -p;
}
void range_add(int l, int r, int x){ //给区间[l, r]加上x
    add(l, x), add(r + 1, -x);
}
int ask(int p){ //单点查询
    ll res = 0;
    while(p) res += sum[p], p -= p & -p;
    return res;
}
int main(){
	return 0;
} 

 区间修改+区间查询

//区间修改+区间查询 
#include<bits/stdc++.h>
using namespace std;
typedef long long ll; 
const ll maxn=1e7+5;
ll d[maxn];//记录原数组的前缀和 
ll sum1[maxn];
ll sum2[maxn];
ll n,M;
void add(ll p, ll x){
    for(int i = p; i <= n; i += i & -i)
        sum1[i] += x, sum2[i] += x * p;
}
void range_add(ll l, ll r, ll x){
    add(l, x), add(r + 1, -x);
}
ll ask(ll p){
    ll res = 0;
    for(int i = p; i; i -= i & -i)
        res += (p + 1) * sum1[i] - sum2[i];
    return res;
}
ll range_ask(ll l, ll r){//l到r的区间和 
    return ask(r) - ask(l - 1);
}
int main(){
	//先不考虑原数组的影响,直接区间修改修改sum1和sum2
    //最后要求[l,r]的区间和的话 
	//结果就是range_ask(l,r)+d[r]-d[l-1]
	// d[r]-d[l-1]考虑原数组的初值 
	return 0;
} 

完结撒花!!!!

猜你喜欢

转载自blog.csdn.net/TDD_Master/article/details/81138542