[BOI2004]Sequence 數字序列

嘟嘟嘟ios

 

這道題要是直接想正解實在太難了,還得從一些特殊的狀況一點點入手。git

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是中位數得證。ide

如今再想一想通常狀況:對於數列{an},可能有一段遞增,有一段遞減,那麼就把這些當作一段一段的:初始每一段的長度都爲1,令中位數爲ci,則ci = ai。若ci < ci+1,那麼就保持不變;不然將ci和ci+1所在的區間合併,取一個新的中位數,做爲新區間的答案。就這樣不斷的合併,直到ci不降低。優化

所以咱們須要一個數據結構,支持合併、查詢最大值和刪除。爲何要查詢最大值和刪除呢?由於維護中位數能夠只維護⌈1/2區間長度⌉小的數,用一個大根堆,則堆頂就是中位數。合併完兩個區間後,就一直刪除堆頂,直到元素個數 = ⌈1/2區間長度⌉。spa

那麼就能想到用左偏樹實現啦。因此左偏樹並非這道題的核心,實際上只起到了一個優化的做用。code

最後一點,就是題中讓求的是嚴格遞增,然而咱們一直是在不降低的前提下思考的。有一個巧妙的作法,就是開始ai -= i,就轉換成了不降低。blog

代碼稍微參考了別人的題解(左偏樹仍是不太熟啊)。get

 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
相關文章
相關標籤/搜索