【进阶——树状数组】 区间求最值
上一篇讲的是区间求和,这一篇讲区间求最值。
首先,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 < 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时,比较结束。
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 }
树状数组区间求和——
http://www.cnblogs.com/mypride/p/5001858.html