问题
树状数组已经可以用于区间修改,区间查询,那能不能把求和的理论运用到最值上面呢?
分析
参照了另一篇讲解
这里重点是简单记录思路,整理代码
这里树状数组的C[]和A[]与求和树状数组的相同,C[y]代表A[ y-lowbit(y)+1 , y ]内的最大值
。
那么用原本的单点维护,我们能得到单次logn,总数nlogn的预处理C[y]
通过观察lowbit的变化规律,发现有一种类似dp的做法。
用
来更新C[i]
为C[i]贡献的只有当存在一个
使
这种数是有限的,简单模拟后发现有x-20,x-21…x-2k这么多,其中2k < lowbit(x)且2(k+1)>=lowbit(x),也就是动的二进制位数小于lowbit(x)
这就有了一种更快的预处理思路,复杂度为O(log2n)
体现在下面的update函数
查询方面,前缀和是不能用的。由于C[y]代表A[ y-lowbit(y)+1 , y ]将其进行改进找出了一种递推的思路:分治逐步缩小范围。
在查询[x,y]间时:
当y-lowbit(y) > x,区间更新为比较
与
区间的最大值,前者是递归,后者是
当y-lowbit(y) <=x,只缩小一,变为比较 与 区间的最大值,这里前者是直接读值,后者是递归
递归可以转化成while,代码见下面的query函数
代码
#include "cstdlib"
#include <iostream>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
#define lowbit(x) (x&(-x))
class Binary_Indexed_Trees_max//区间最大值
{
private:
const static int MAXN = 1010;
int A[MAXN];//原数组
int C[MAXN];
int N;
public:
void init(int* a, int n)//数组,个数
{
N = n;
for (int i = 1; i <= n; i++)
{
A[i] = *(a+i-1);
}
}
void update(int x)
{
int lx, i;
while (x <= N)
{
C[x] = A[x];
lx = lowbit(x);
for (i = 1; i < lx; i <<= 1)
C[x] = max(C[x], C[x - i]);
x += lowbit(x);
}
}
int query(int x, int y)
{
int ans = 0;
while (y >= x)
{
ans = max(A[y], ans);
y--;
for (; y - lowbit(y) >= x; y -= lowbit(y))
ans = max(C[y], ans);
}
//C[y]是[ y-lowbit(y)+1 , y ]内的最大值
//y-lowbit(y) > x ,则query(x,y) = max( C[y] , query(x, y-lowbit(y)) );
//y-lowbit(y) <=x,则query(x,y) = max( A[y] , query(x, y-1);
return ans;
}
}BIT;
int arr[1000] = {8,2,5,4,3,7,7,9,1,10};
int main()
{
ios::sync_with_stdio(false);
BIT.init(arr, 10);
for (int i = 1; i <= 10; i++)
{
BIT.update(i);
}
for (int i = 1; i <= 10; i++)
{
cout << BIT.query(1, i) << endl;//求出1倒i间arr的最大值
}
return 0;
}