您的位置:首页 > 其它

【进阶——树状数组】 区间求最值

2015-11-28 13:28 316 查看
上一篇讲的是区间求和,这一篇讲区间求最值。

首先,a[]数组仍然是保存原始数据。但是c[]数组变了,c[i]将会保存从a[1]到a[i]的最值。

初始化c[]:

当我们输入a[i]时,c[i]需要需要向前依次枚举被c[i]所包含的c[]数组。比如,当i == 8时,需要向前依次枚举c[7], c[6], c[4],取a[8]与这几个c[]中的最值保存在c[8]中。找到c[]的规律了没有?依次是i-1, i-2, i-4...

void Init()
{
int  y;
memset(c, 0, sizeof(c));
for(int i = 1; i <= n; i++)
{
scanf("%d", &y);
a[i] = y;
c[i] = y;
for(int j = 1; j <lowbit(i); j <<= 1)           //需要比较的c[]
c[i] = c[i] > c[i-j] ? c[i] : c[i-j];
}
}


可以看出,每输入一个a[i],处理c[i]的时间复杂度为log2(n),输入n个,初始化的时间复杂度就是n*log2(n)。

修改c[]:

我们改变了一个a[i],那么就需要修改所有与a[i],相关的c[]。修改每个的c[]的方法可以用上面初始化的方法,而需要修改的c[]可以用区间求和里的一段代码确定。

void Maxn(int x, int y)
{
a[x] = y;
for(int i = x; i <= n; i += lowbit(i))              //需要修改的c[]
{
c[i] = y;
for(int j = 1; j < lowbit(i); j <<= 1)          //修改时需要比较的c[]
{
c[i] = c[i] > c[i-j] ? c[i] : c[i-j];
}
}
}


每次修改的时间复杂度近似为log2(n)*log2(n)。

查询:

查询从a[i]到a[j]之间的最值(i <= j)。我们不能直接查看c[j],因为也许c[j]中包含的区间[l, r]中l < i或l > i,c[j]不能恰好包含区间[i, j]。

因此,当l < i时,我们就取a[j]与当前已经取到的最值比较,如果a[j]满足取代条件,就用a[j]取代当前最值。

当l >= i,我们取c[j]与当前最值比较,如果c[j],满足取代条件,就用c[j]取代当前最值。

当l == i时,比较结束。

void Query(int l, int r)
{
int ans = 0;
while(1)
{
ans = ans > a[r] ? ans : a[r];
if(r == l) break;
for(r -= 1; r-l >= lowbit(r); r -= lowbit(r))
ans = ans > c[r] ? ans : c[r];
}
printf("%d\n", ans);
}


时间复杂度近似为log2(n)。

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 200010;

int a
, c
;
int t, n, m;

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

void Maxn(int x, int y)
{
a[x] = y;
for(int i = x; i <= n; i += lowbit(i))              //需要修改的c[]
{
c[i] = y;
for(int j = 1; j < lowbit(i); j <<= 1)          //修改时需要比较的c[]
{
c[i] = c[i] > c[i-j] ? c[i] : c[i-j];
}
}
}

void Init()
{
int  y;
memset(c, 0, sizeof(c));
for(int i = 1; i <= n; i++)
{
scanf("%d", &y);
a[i] = y;
c[i] = y;
for(int j = 1; j <lowbit(i); j <<= 1)           //需要比较的c[]
c[i] = c[i] > c[i-j] ? c[i] : c[i-j];
}
}

void Query(int l, int r)
{
int ans = 0;
while(1)
{
ans = ans > a[r] ? ans : a[r];
if(r == l) break;
for(r -= 1; r-l >= lowbit(r); r -= lowbit(r))
ans = ans > c[r] ? ans : c[r];
}
printf("%d\n", ans);
}

void Work()
{
char s[2];
int x, y;
for(int i = 1; i <= m; i++)
{
scanf("%s%d%d", s, &x, &y);
if(s[0] == 'U') Maxn(x, y);
else if(s[0] == 'Q') Query(x, y);
}
}

int main()
{
//freopen("test.in", "r", stdin);
while(~scanf("%d%d", &n, &m))
{
Init();
Work();
}
}


mod

树状数组区间求和——
/article/5857189.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: