这道题目一看就是要求在每个数前面的一段前缀中的前驱和后继。
很直白地就会想到二叉平衡树。
对于每一天的最小波动值只需要在二叉平衡树中插入节点的时候记录前驱和后继就可以很方便地算出来了。
所以总的来说,这道题就是一道模板题
先贴上treap的板子吧。。
1 //treap 2 #include <bits/stdc++.h> 3 4 using namespace std; 5 6 const int N = 32768; 7 8 struct treap { 9 int fa; 10 int s[2]; 11 int wei; 12 int key; 13 14 friend void check_print(const treap a) { 15 printf("!%d %d %d %d %d ", a.fa, a.s[0], a.s[1], a.wei, a.key); 16 } 17 }tp[N + 1]; 18 19 int n; 20 int root, tot; 21 int ans; 22 23 void rotate(int x) { 24 int y = tp[x].fa; 25 int z = tp[y].fa; 26 int c = tp[y].s[1] == x; 27 tp[x].fa = z; 28 if(z) tp[z].s[tp[z].s[1] == y] = x; 29 tp[tp[x].s[c ^ 1]].fa = y; 30 tp[y].s[c] = tp[x].s[c ^ 1]; 31 tp[x].s[c ^ 1] = y; 32 tp[y].fa = x; 33 if(root == y) root = x; 34 } 35 36 void set_up(int k, int f, int x) { 37 tp[k].key = x; 38 tp[k].wei = rand(); 39 tp[k].fa = f; 40 tp[f].s[x > tp[f].key] = k; 41 } 42 43 void up(int k) { 44 int f = tp[k].fa; 45 while(f && tp[f].wei > tp[k].wei) { 46 rotate(k); 47 f = tp[k].fa; 48 } 49 } 50 51 int insert(int k, int x, int b, int s) { 52 if(tp[k].key == x) return 0; 53 if(tp[k].key > x) s = min(s, tp[k].key); 54 if(tp[k].key < x) b = max(b, tp[k].key); 55 if(!k || !tp[k].s[x > tp[k].key]) { 56 set_up(++tot, k, x); 57 up(tot); 58 if(!k) root = tot; 59 return min(abs(b - x), abs(s - x)); 60 } 61 return insert(tp[k].s[tp[k].key < x], x, b, s); 62 } 63 64 int main() { 65 srand(time(NULL)); 66 int a; 67 scanf("%d%d", &n, &a); 68 ans += a; 69 insert(root, a, -2e9, 2e9); 70 for(int i = 1; i < n; i++) { 71 scanf("%d", &a); 72 ans += insert(root, a, -2e9, 2e9); 73 } 74 printf("%d ", ans); 75 return 0; 76 }
但是C++的STL库中有一个神奇的东西——set。
它完美的(带了巨大常数)实现了高难度的红黑树。
既然有这样的好东西,为何不用呢?
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 const int N = 100000; 6 7 int n; 8 int a; 9 int ans; 10 set<int>s; 11 12 int main() { 13 scanf("%d", &n); 14 scanf("%d", &a); 15 s.insert(2e9); 16 s.insert(-2e9); 17 s.insert(2e9 + 1); 18 s.insert(-2e9 - 1); 19 s.insert(a); 20 ans += a; 21 for(int i = 1; i < n; i++) { 22 int k1 = 1e9, k2 = 1e9, k3 = 1e9, a; 23 scanf("%d", &a); 24 set<int>::iterator it = s.upper_bound(a); 25 k1 = abs(a - *it); 26 k2 = abs(a - *it), it--; 27 k3 = abs(a - *it), it++; 28 ans += min(k1, min(k2, k3)); 29 s.insert(a); 30 } 31 printf("%d ", ans); 32 return 0; 33 }
至于set的用法,这里给大家推荐一个个人认为归纳的比较好的博客,可以去借鉴一下。