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
", query(a, b, 1, n, 1));
92         }
93         puts("");
94     }
95     return 0;
96 }
View Code

链接:http://acm.hrbust.edu.cn/index.php?m=ProblemSet&a=showProblem&problem_id=1832

外传野史:这道题其实是某学长的面试题,当时那个学长太紧张了,没想清楚,只想到了可以用线段树维护最大值和最小值来解,但是面试官貌似也不认识线段树,然后提示说这个是股票,最小值必须在最大值的左面,这样单纯的维护最大值最小值就不好使了。

所以正解的确是用线段树,但是是用线段树维护差值,并用RMQ来帮忙维护 差值 = 右子树指定范围内最大值-左子树指定范围内最小值 的问题。

这个是zz想到的啦,给他一个good~

原文地址:https://www.cnblogs.com/Griselda/p/3433671.html