[BOI2004]Sequence 数字序列

嘟嘟嘟

 

这道题要是直接想正解实在太难了,还得从一些特殊的情况一点点入手。

1.如果ai本身就是递增的,那么令bi = ai即最优解。

2.如果ai严格递减,则b1 = b2 = b3 = ……= bn = 中位数为最优解。这个可以用初中的几何证明:把 |bi - ai| 想象成数轴上两点间距离,那么 |x - a1| + |x - a2|的最优解x一定a1和a2之间;扩展到三项:|x - a1| + |x - a2| + |x - a3|,那么x就是中位数;以此类推,x是中位数得证。

现在再想想一般情况:对于数列{an},可能有一段递增,有一段递减,那么就把这些看成一段一段的:初始每一段的长度都为1,令中位数为ci,则ci = ai。若ci < ci+1,那么就保持不变;否则将ci和ci+1所在的区间合并,取一个新的中位数,作为新区间的答案。就这样不断的合并,直到ci不下降。

因此我们需要一个数据结构,支持合并、查询最大值和删除。为什么要查询最大值和删除呢?因为维护中位数可以只维护⌈1/2区间长度⌉小的数,用一个大根堆,则堆顶就是中位数。合并完两个区间后,就一直删除堆顶,直到元素个数 = ⌈1/2区间长度⌉。

那么就能想到用左偏树实现啦。所以左偏树并不是这道题的核心,实际上只起到了一个优化的作用。

最后一点,就是题中让求的是严格递增,然而我们一直是在不下降的前提下思考的。有一个巧妙的做法,就是开始ai -= i,就转换成了不下降。

代码稍微参考了别人的题解(左偏树还是不太熟啊)。

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<cstring>
 6 #include<cstdlib>
 7 #include<cctype>
 8 #include<vector>
 9 #include<stack>
10 #include<queue>
11 using namespace std;
12 #define enter puts("") 
13 #define space putchar(' ')
14 #define Mem(a, x) memset(a, x, sizeof(a))
15 #define rg register
16 typedef long long ll;
17 typedef double db;
18 const int INF = 0x3f3f3f3f;
19 const db eps = 1e-8;
20 const int maxn = 1e6 + 5;
21 inline ll read()
22 {
23   ll ans = 0;
24   char ch = getchar(), last = ' ';
25   while(!isdigit(ch)) {last = ch; ch = getchar();}
26   while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();}
27   if(last == '-') ans = -ans;
28   return ans;
29 }
30 inline void write(ll x)
31 {
32   if(x < 0) x = -x, putchar('-');
33   if(x >= 10) write(x / 10);
34   putchar(x % 10 + '0');
35 }
36 
37 int n;
38 
39 struct Node
40 {
41   int root, l, r, siz;
42   ll val;
43 }st[maxn];
44 int top = 0;
45 
46 ll val[maxn];
47 int dis[maxn], ls[maxn], rs[maxn];
48 int merge(int x, int y)
49 {
50   if(!x || !y) return x | y;
51   if(val[x] < val[y]) swap(x, y);
52   rs[x] = merge(rs[x], y);
53   if(dis[rs[x]] > dis[ls[x]]) swap(rs[x], ls[x]);
54   dis[x] = dis[rs[x]] + 1;
55   return x;
56 }
57 int Del(int x)
58 {
59   return merge(ls[x], rs[x]);
60 }
61 
62 ll ans = 0;
63 
64 int main()
65 {
66   n =  read();
67   for(int i = 1; i <= n; ++i) val[i] = read() - i;
68   st[++top] = (Node){1, 1, 1, 1, val[1]};
69   for(int i = 2; i <= n; ++i)
70     {
71       st[++top] = (Node){i, i, i, 1, val[i]};
72       while(top && st[top].val < st[top - 1].val)
73     {
74       top--;
75       st[top].root = merge(st[top].root, st[top + 1].root);
76       st[top].siz += st[top + 1].siz;
77       st[top].r = st[top + 1].r;
78       while(st[top].siz > (st[top].r - st[top].l + 1 + 1) >> 1)    //向上取整 
79         {
80           st[top].siz--;
81           st[top].root = Del(st[top].root);
82         }
83       st[top].val = val[st[top].root];
84     }
85     }
86   for(int i = 1, j = 1; i <= n; ++i)
87     {
88       if(i > st[j].r) j++;
89       ans += abs(st[j].val - val[i]);
90     }
91   write(ans); enter;
92   for(int i = 1, j = 1; i <= n; ++i)
93     {
94       if(i > st[j].r) j++;
95       write(st[j].val + i), space;
96     }
97   enter;
98   return 0;
99 }
View Code

 

posted @ 2018-10-16 20:31  mrclr  阅读(432)  评论(1编辑  收藏  举报