一维树状数组

树状数组这种数据结构利用二进制来优化区间。

假设我们要维护区间 [1,n]

定义树状数组的第i个元素维护了区间[i&(i-1)+1,i]

 

那么他究竟可以解决哪些区间问题?

先考虑点更新,区间查询:

点更新的话要解一个不定不等式:i&(i-1)<=x<=i

        如果更新点x,就要更新树状数组对应的所有第i个元素

        可以证明如果i1是一个解,且存在另一个大于i1的最小的解i2,则i2=i1+i1&(-i1)。笔者不想证明。

区间查询因情况不定:

         既然定义树状数组的第i个元素维护了区间[i&(i-1)+1,i],那么第j=i&(i-1)个元素将维护区间[j&(j-1)+1,i&(i-1)]......

        可以证明任何一个前缀区间都可以用有限(Olgn)个树状数组元素来维护。笔者不想证明。

        故我们可以很容易解决前缀区间和,前缀区间最值。

        任意区间和满足区间减法,容易处理。

        任意区间最值却不满足,但我们可以递归处理。

                利用原区间与树状数组共同维护

                若x<=y&(-y)

                则query(x,y)->query(x,y&(-y))和query(y&(-y)+1,y);

                若x>y&(-y)

                则query(x,y)->query(x,y-1)和y这个点的单点值

                但建议迭代实现

再考虑区间更新点查询

这个常用于区间和,

对于序列a[1],a[2],a[3]...(以后简写为序列a)用于维护区间[1,N];定义序列a的前i项和表示我们维护区间的点i的值。

可以证明序列a唯一存在,笔者不想证明。

        如果我们要更新区间[x,y],只用修改a[x]和a[y+1]的值

        如果我们要查询区间点i的值,只用查询序列a的前i项和

这里已经转移到对新序列a的点修改区间查询了,over

最后考虑区间更新区间查询

这个也常用于区间和,

此处序列a与上面的序列a一样。

        如果我们要更新区间[x,y],只用修改a[x]和a[y+1]的值

        如果我们要查询区间[1,i ],只用查询序列a的前i项和的前i项和(多几个字)

                可以证明下述结论

               \sum_{i=1}^{N}\sum_{j=1}^{i} a_{j}=\sum_{j=1}^{N} (N+1-j)a_{j} =(N+1)\sum_{j=1}^{N} a_{j}-\sum_{j=1}^{N} ja_{j}

                很明显变成了查询序列a的前缀和以及序列a[1],2a[2],3a[3],4a[4]...的前缀和

                那么我们再为序列a[1],2a[2],3a[3],4a[4]...也搞一个点更新区间查询树状数组来维护就行了

        于是前缀区间查询已经解决,任意区间查询用区间减法就可以得到

最后附上代码

//点更新区间查询,最大可用范围[1,N),实际使用[1,n]
//初始化N,n,初始化tree[]为0
//函数输入,函数返回值输出
const int N;
int n;
int tree[N];

void add(int k,int d){
    for(int i=k;i<=n;i+=i&-i)tree[i]+=d;
}
int sigma(int k){
    int ret=0;
    for(int i=k;i;i-=i&-i)ret+=tree[i];
    return ret;
}
int sigma(int u,int v){
    return sigma(v)-sigma(u-1);
}
///////////////////////////////////////////////////////////////

//区间更新点查询,最大可用范围[1,N),实际使用[1,n]
//初始化N,n初始化tree[]为0
//函数输入,函数返回值输出
const int N;
int n;
int tree[N];

void add(int k,int d){
    for(int i=k;i<=n;i+=i&-i)tree[i]+=d;
}
void add(int u,int v,int d){
    add(u,d);
    add(v+1,-d);
}
int sigma(int k){
    int ret=0;
    for(int i=k;i;i-=i&-i)ret+=tree[i];
    return ret;
}
/////////////////////////////////////////////////////////////////



//区间更新区间查询,最大可用范围[1,N),实际使用[1,n]
//初始化N,n初始化tree[]、tree2[]为0
//函数输入,函数返回值输出
const int N;
int n;
int tree[N],tree2[N];

void add(int k,int d){
    for(int i=k;i<=n;i+=i&-i)tree[i]+=d,tree2[i]+=k*d;
}
void add(int u,int v,int d){
    add(u,d);
    add(v+1,-d);
}
int sigma(int k){
    int ret=0;
    for(int i=k;i;i-=i&-i)ret+=(k+1)*tree[i]-tree2[i];
    return ret;
}
int sigma(int u,int v){
    return sigma(v)-sigma(u-1);
}
///////////////////////////////////////////////////////////////

猜你喜欢

转载自blog.csdn.net/qq_41157212/article/details/82822166