首先ORZ一發Claris聚聚的題解:http://www.cnblogs.com/clrs97/p/8689215.html,否則我可能沒機會補過這道神題了。php
這裏寫一個更詳細的題解吧(我仍是太菜了啊)。html
有\(n(n \le10^5)\)我的依次進入一個入口,要到一個出口。入口到出口有兩條一樣長的路。每一個人都有一個速度,用通行時間\(a_i(1\le a_i \le 10^6)\)表示,他能夠選擇任一條路走。可是,若走這條路的前面的人比他慢的話,他只能降到和前面全部人最慢的那我的一樣的速度(從而會多花時間)。如今請規劃每一個人選哪條路,使得每一個人因等前面的人而浪費的時間儘量少。數據結構
Sample Input優化
5
100 4 3 2 1
Sample Outputspa
6
此題很容易用DP來作。考慮前\(i\)我的,則兩個樓梯必有一個的通行時間變爲前\(i\)我的最慢的那個,咱們設\(dp[i][j]\)表示前\(i\)我的另外一個樓梯當前通行時間是\(j\)(\(j\)從小到大離散化)時的最優答案,則考慮\(dp[i+1]\)和\(dp[i]\)的關係:code
(1)若\(a[i+1] \ge max(a[1..i])\),則顯然\(dp[i+1][j]=dp[i][j]\);htm
(2)若\(a[i+1]<max(a[1..i])\),則:blog
狀況1:\(j\)對應狀態快的那個樓梯比\(a[i+1]\)時間短,且選這個樓梯,因而\(dp[i+1][k]=min(dp[i][j],j\le k)\),其中\(k\)爲\(a[i+1]\)離散化的結果;get
狀況2:\(j\)對應狀態快的那個樓梯比\(a[i+1]\)時間短,但選最慢的樓梯,因而\(dp[i+1][j]=dp[i][j]+max(a[1..i])-a[i+1]\),其中\(j<k\);string
狀況3:\(j\)對應狀態快的那個樓梯比\(a[i+1]\)的時間長,那必然選這個樓梯,因而\(dp[i+1][j]=dp[i][j]+f[j]-a[i+1]\),其中\(j>k\),\(f[j]\)表示第\(j\)小的值。
這樣狀態數和轉移複雜度均爲\(n^2\)。下面考慮數據結構優化。
咱們須要維護的dp要支持區間最小值查詢,單點修改,區間增長,和區間\(dp[i][j]+=f[j]\)。
若是沒有最後的操做此題直接用線段樹就簡單多了。
加上了這種操做,考慮分塊。每塊首先要維護增量tag,該tag對最值無影響。下面主要考慮\(dp[i][j]+=f[j]\)。
注意到一個性質:若\((dp[i][j+1]-dp[i][j])/(f[j+1]-f[j])<(dp[i][j]-dp[i][j-1])/(f[j]-f[j-1])\),那麼不管再怎麼增長\(dp[i][j]\)也不可能最優。因此將\(j\)下標看作二維點\((f[j],dp[i][j])\)後,全部可能的最優值造成一個下凸殼。當整塊\(dp[i][j]+=f[j]\)後,凸殼上還是這些點,但最小值點可能將向左移動。因而咱們只要不斷刪除凸殼右邊的點,就能夠每一塊均攤\(O(1)\)的修改和查詢最小值。
對於單點修改,只須要重構凸殼,複雜度爲塊大小。
如今考慮分塊後從\(dp[i]\)轉移到\(dp[i+1]\)的總複雜度,設塊大小\(b\)。因爲單點修改僅一個點\(k\),故複雜度\(b\);取最小值複雜度\(b+n/b\);區間加複雜度\(b+n/b\);區間\(dp[i][j]+=f[j]\)複雜度\(b+n/b\)。當\(b\)取\(\sqrt n\) 時複雜度最優,爲\(\sqrt n \)。考慮到重構凸殼較慢,應在求最值時如須要再重構凸殼。
總時間複雜度\(O(n \sqrt n)\)
1 #include<cstdio> 2 #include<cmath> 3 #include<algorithm> 4 #include<cstring> 5 using namespace std; 6 #define LL long long 7 struct Block{ 8 LL a[400], tag, delta; 9 int order[400]; 10 int pos[400], back, n; 11 bool flag; 12 void init(int b[], int size){ 13 n = size; flag = true; 14 memcpy(order, b, sizeof(int)*n); 15 memset(a, 0x3f, sizeof(LL)*n); 16 } 17 bool check(int j1, int j, int j2){ 18 return (a[j2] - a[j]) * (order[j] - order[j1]) <= (a[j] - a[j1]) * (order[j2] - order[j]); 19 } 20 LL get(int i){ return a[i] + tag * order[i] + delta; } 21 void update(){ 22 for (int i = 0; i < n; i++) 23 a[i] = get(i); 24 tag = delta = 0; back = 0; 25 flag = false; 26 for (int i = 0; i < n; i++){ 27 while (back>1 && check(pos[back - 1], pos[back], i))back--; 28 pos[++back] = i; 29 } 30 while (back > 1 && get(pos[back - 1]) <= get(pos[back]))back--; 31 } 32 void set(int i, LL val){ 33 a[i] += val - get(i); 34 flag = true; 35 } 36 void add(int l, int r, int d){ 37 if (l == 0 && r == n - 1)delta += d; 38 else{ 39 for (int i = l; i <= r; i++) 40 a[i] += d; 41 flag = true; 42 } 43 } 44 void add2(int l, int r){ 45 if (l == 0 && r == n - 1){ 46 tag++; 47 while (back > 1 && get(pos[back - 1]) <= get(pos[back]))back--; 48 } 49 else{ 50 for (int i = l; i <= r; i++) 51 a[i] += order[i]; 52 flag = true; 53 } 54 } 55 LL queryMin(int l, int r){ 56 if (l == 0 && r == n - 1){ 57 if (flag)update(); 58 return get(pos[back]); 59 } 60 LL ret = 1LL << 60; 61 for (int i = l; i <= r; i++) 62 ret = min(ret, get(i)); 63 return ret; 64 } 65 }b[1000]; 66 int a[100002], order[100002]; 67 int belong[100002], offset[100002], blockSize; 68 void add(int l, int r, int delta){ 69 int start = l / blockSize, end = r / blockSize; 70 for (int i = start; i <= end; i++) 71 b[i].add(i == start ? offset[l] : 0, i == end ? offset[r] : b[i].n - 1, delta); 72 } 73 void add2(int l, int r){ 74 int start = l / blockSize, end = r / blockSize; 75 for (int i = start; i <= end; i++) 76 b[i].add2(i == start ? offset[l] : 0, i == end ? offset[r] : b[i].n - 1); 77 } 78 LL queryMin(int l, int r){ 79 int start = l / blockSize, end = r / blockSize; 80 LL ret = 1LL << 60; 81 for (int i = start; i <= end; i++) 82 ret = min(ret, b[i].queryMin(i == start ? offset[l] : 0, i == end ? offset[r] : b[i].n - 1)); 83 return ret; 84 } 85 int main(){ 86 int n; 87 scanf("%d", &n); 88 for (int i = 1; i <= n; i++){ 89 scanf("%d", &a[i]); 90 order[i] = a[i]; 91 } 92 order[0] = 0; 93 sort(order, order + n + 1); 94 int cnt = unique(order, order + n + 1) - order; 95 blockSize = sqrt(cnt); 96 int j = 0, k = 0; 97 for (int i = 0; i < cnt; i++){ 98 belong[i] = k; 99 offset[i] = j++; 100 if (j == blockSize){ 101 b[k].init(order + i - j + 1, j); 102 j = 0; k++; 103 } 104 } 105 if (j)b[k].init(order + cnt - j, j); 106 b[0].set(0, 0); 107 int mpos = 0; 108 for (int i = 1; i <= n; i++){ 109 int pos = lower_bound(order, order + cnt, a[i]) - order; 110 if (pos >= mpos)mpos = pos; 111 else{ 112 LL val = queryMin(0, pos); 113 b[belong[pos]].set(offset[pos], val); 114 add(0, pos - 1, order[mpos] - order[pos]); 115 add(pos + 1, mpos, -order[pos]); 116 add2(pos + 1, mpos); 117 } 118 } 119 printf("%lld", queryMin(0, cnt - 1)); 120 }