Hlg 1832 【线段树 && RMQ】.cpp
题意:
在给出的区间内求出最大买进卖出的差价。
思路:
对于弱数据:维护一个从左到右的最大差价和最小值。即当发现当前值比最小值小的时候更新最小值,否则看一下当前值与之前最小值的差价是否比最大差价大,是就更新最大差价。时间复杂度是O(m*n)
对于强数据:利用线段树维护一个最大差价、最大值和最小值,查询的时候求出询问的范围内左右子树的最大差价,然后再利用RMQ求出[l, mid]的最小值和[mid+1, r]的最大值,然后返回max(df[rt<<1], df[rt<<1|1], RMQ(mid+1, r)-RMQ(l, mid));这个的时间复杂度是O(nlgn)+m*O(nlgn)
Tips:
我的做法是维护了最大值和最小值以便求出最大差值,也可以不维护这个,直接利用RMQ求出最大值和最小值,然后求最大差值。
Code:
1 #include <stdio.h> 2 #include <cstring> 3 #include <algorithm> 4 #include <cmath> 5 using namespace std; 6 7 const int MAXN = 100010; 8 int mx[MAXN<<2], mn[MAXN<<2], df[MAXN<<2]; 9 int dpx[MAXN][25], dpn[MAXN][25]; 10 int val[MAXN]; 11 12 13 void makermq(int n) 14 { 15 for (int j = 1; (1<<j) <= n; ++j) 16 for (int i = 1; i+(1<<j)-1 <= n; ++i) { 17 dpn[i][j] = min(dpn[i][j-1], dpn[i+(1<<(j-1))][j-1]); 18 dpx[i][j] = max(dpx[i][j-1], dpx[i+(1<<(j-1))][j-1]); 19 } 20 } 21 22 int rmqx(int s, int v) 23 { 24 int k = (int)(log((v-s+1)*1.0)/log(2.0)); 25 return max(dpx[s][k], dpx[v-(1<<k)+1][k]); 26 } 27 28 int rmqn(int s, int v) 29 { 30 int k = (int)(log((v-s+1)*1.0)/log(2.0)); 31 return min(dpn[s][k], dpn[v-(1<<k)+1][k]); 32 } 33 34 void PushUp(int rt) 35 { 36 mx[rt] = max(mx[rt<<1], mx[rt<<1|1]); 37 mn[rt] = min(mn[rt<<1], mn[rt<<1|1]); 38 df[rt] = max(df[rt<<1], df[rt<<1|1]); 39 df[rt] = max(df[rt], mx[rt<<1|1]-mn[rt<<1]); 40 } 41 42 void Build(int rt, int l, int r) 43 { 44 if (l == r) { 45 scanf("%d", &mx[rt]); 46 mn[rt] = mx[rt]; 47 dpn[l][0] = dpx[l][0] = mx[rt]; 48 return; 49 } 50 int mid = (l+r)/2; 51 Build(rt<<1, l, mid); 52 Build(rt<<1|1, mid+1, r); 53 PushUp(rt); 54 } 55 56 int query(int l, int r, int L, int R, int rt) 57 { 58 if (L >= l && R <= r) { 59 return df[rt]; 60 } 61 int mid = (L+R)/2; 62 int ans = 0; 63 if (r <= mid) ans = query(l, r, L, mid, rt<<1); 64 else if (l > mid) ans = query(l, r, mid+1, R, rt<<1|1); 65 else { 66 ans = query(l, r, L, mid, rt<<1); 67 ans = max(ans, query(l, r, mid+1, R, rt<<1|1)); 68 69 ans = max(ans, rmqx(mid+1, r)-rmqn(l, mid)); 70 } 71 return ans; 72 } 73 74 int main() 75 { 76 //freopen("in.txt", "r", stdin); 77 int n; 78 int q, a, b; 79 while (~scanf("%d", &n)) { 80 n++; 81 memset(df, 0, sizeof(df)); 82 memset(mx, 0, sizeof(mx)); 83 memset(mn, 0, sizeof(mn)); 84 Build(1, 1, n); 85 makermq(n); 86 87 scanf("%d", &q); 88 while (q--) { 89 scanf("%d %d", &a, &b); 90 a++, b++; 91 printf("%d\n", query(a, b, 1, n, 1)); 92 } 93 puts(""); 94 } 95 return 0; 96 }
链接:http://acm.hrbust.edu.cn/index.php?m=ProblemSet&a=showProblem&problem_id=1832
外传野史:这道题其实是某学长的面试题,当时那个学长太紧张了,没想清楚,只想到了可以用线段树维护最大值和最小值来解,但是面试官貌似也不认识线段树,然后提示说这个是股票,最小值必须在最大值的左面,这样单纯的维护最大值最小值就不好使了。
所以正解的确是用线段树,但是是用线段树维护差值,并用RMQ来帮忙维护 差值 = 右子树指定范围内最大值-左子树指定范围内最小值 的问题。
这个是zz想到的啦,给他一个good~