树状数组这种数据结构利用二进制来优化区间。
假设我们要维护区间 [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项和(多几个字)
可以证明下述结论
很明显变成了查询序列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);
}
///////////////////////////////////////////////////////////////