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

上一篇讲的是区间求和,这一篇讲区间求最值。

首先,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...

 1 void Init()
 2 {
 3     int  y;
 4     memset(c, 0, sizeof(c));
 5     for(int i = 1; i <= n; i++)
 6     {
 7         scanf("%d", &y);
 8         a[i] = y;
 9         c[i] = y;
10         for(int j = 1; j <lowbit(i); j <<= 1)           //需要比较的c[]
11             c[i] = c[i] > c[i-j] ? c[i] : c[i-j];
12     }
13 }

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

 

修改c[]

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

 

 1 void Maxn(int x, int y)
 2 {
 3     a[x] = y;
 4     for(int i = x; i <= n; i += lowbit(i))              //需要修改的c[]
 5     {
 6         c[i] = y;
 7         for(int j = 1; j < lowbit(i); j <<= 1)          //修改时需要比较的c[]
 8         {
 9             c[i] = c[i] > c[i-j] ? c[i] : c[i-j];
10         }
11     }
12 }

 

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

 

查询:

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

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

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

l == i时,比较结束。

 

 1 void Query(int l, int r)
 2 {
 3     int ans = 0;
 4     while(1)
 5     {
 6         ans = ans > a[r] ? ans : a[r];
 7         if(r == l) break;
 8         for(r -= 1; r-l >= lowbit(r); r -= lowbit(r))
 9             ans = ans > c[r] ? ans : c[r];
10     }
11     printf("%d\n", ans);
12 }

 

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

 

 1 #include <cstdio>
 2 #include <cmath>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6 
 7 const int N = 200010;
 8 
 9 int a[N], c[N];
10 int t, n, m;
11 
12 int lowbit(int x)
13 {
14     return x&(-x);
15 }
16 
17 void Maxn(int x, int y)
18 {
19     a[x] = y;
20     for(int i = x; i <= n; i += lowbit(i))              //需要修改的c[]
21     {
22         c[i] = y;
23         for(int j = 1; j < lowbit(i); j <<= 1)          //修改时需要比较的c[]
24         {
25             c[i] = c[i] > c[i-j] ? c[i] : c[i-j];
26         }
27     }
28 }
29 
30 void Init()
31 {
32     int  y;
33     memset(c, 0, sizeof(c));
34     for(int i = 1; i <= n; i++)
35     {
36         scanf("%d", &y);
37         a[i] = y;
38         c[i] = y;
39         for(int j = 1; j <lowbit(i); j <<= 1)           //需要比较的c[]
40             c[i] = c[i] > c[i-j] ? c[i] : c[i-j];
41     }
42 }
43 
44 void Query(int l, int r)
45 {
46     int ans = 0;
47     while(1)
48     {
49         ans = ans > a[r] ? ans : a[r];
50         if(r == l) break;
51         for(r -= 1; r-l >= lowbit(r); r -= lowbit(r))
52             ans = ans > c[r] ? ans : c[r];
53     }
54     printf("%d\n", ans);
55 }
56 
57 void Work()
58 {
59     char s[2];
60     int x, y;
61     for(int i = 1; i <= m; i++)
62     {
63         scanf("%s%d%d", s, &x, &y);
64         if(s[0] == 'U') Maxn(x, y);
65         else if(s[0] == 'Q') Query(x, y);
66     }
67 }
68 
69 int main()
70 {
71     //freopen("test.in", "r", stdin);
72     while(~scanf("%d%d", &n, &m))
73     {
74         Init();
75         Work();
76     }
77 }
mod

 

树状数组区间求和——
http://www.cnblogs.com/mypride/p/5001858.html

 

转载于:https://www.cnblogs.com/mypride/p/5002556.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值