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 }
View Code

 

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

 

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

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

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

你可能感兴趣的:(线段树)