好,我一直覺得書架是splay,而後發現還有個優化DP的書架。妃的書架數組
藍書和PPT上面都講了,應該比較經典吧。ide
題意:函數
有n個物品,每一個都有寬,高。優化
把它們分紅若干段,使得每段的最大值的總和最小。且每段的總寬度不超過L。spa
n <= 100000code
解:blog
首先有個很顯然的DP是f[i] = min(f[j] + max(j + 1, i)), sum[i] - sum[j] <= L隊列
而後如何優化呢?由於有max函數(非線性)因此難以單調隊列。把決策打出來發現不單調。機關用盡???get
發現當i固定的時候,max(j + 1, i)是一段一段單減的。這樣至關於能肯定狀態轉移方程中的後者。it
再看前者,f[j]這個東西,實際上是單調不減的。證實:把f[j - 1]按照f[j]的方式劃分便可<=f[j]。
怎麼利用呢?樸素的想法是把每個數前面最大的數用單調棧求出來,記爲to[]數組。
那麼每次轉移的時候跳to[]便可。能夠發如今區間(to[i], i)之間的轉移都不優於to[i]。
這裏忽視了一個小問題,有個寬度限制在這裏。個人解決辦法是用pos記錄最先的一個可以轉移過來的位置。pos的維護顯然是線性的。
這個好想好寫的東西最壞複雜度仍是n²,遞增序列就能卡掉,然而交上去有90分......
來考慮正解。咱們能不能每次不跳to[]鏈,而是更快的求出最小值來轉移呢?
很容易(困難)想到用堆維護。對於失效的轉移用延遲刪除法。
問題就只剩如何判斷轉移失效了。
若是轉移的j < pos,顯然不行。此外,若是j不在以i開頭的to鏈上,也是不行的。
有個樸素的想法是用數組維護是否在to鏈上,即每次把i前面比i小的捨去,可是又會被遞增卡成n²。
仔細思考,發現以i開頭的to鏈就是單調棧在處理到i時的棧內元素。
因而咱們一邊DP一邊維護單調棧,只需判斷j是在否在棧中便可。
至此,時間複雜度優化爲nlogn,能夠經過此題。
記得開long long
1 #include <cstdio> 2 #include <algorithm> 3 #include <queue> 4 #define mp std::make_pair 5 6 typedef long long LL; 7 const int N = 100010; 8 9 LL f[N], sum[N]; 10 int st[N][25], pow[N], n, to[N], p[N], top; 11 bool in_stk[N]; 12 std::priority_queue<std::pair<LL, int> > Q; 13 14 inline void STinit() { 15 int j = 1, lm = 0; 16 while((1 << lm) <= n) { 17 while(j < (1 << (lm + 1)) && j <= n) { 18 pow[j] = lm; 19 j++; 20 } 21 lm++; 22 } 23 for(int j = 1; j < lm; j++) { 24 for(int i = 1; i + (1 << j) - 1 <= n; i++) { 25 st[i][j] = std::max(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]); 26 } 27 } 28 return; 29 } 30 31 inline int getmax(int l, int r) { 32 int t = pow[r - l + 1]; 33 return std::max(st[l][t], st[r - (1 << t) + 1][t]); 34 } 35 36 int main() { 37 int L; 38 scanf("%d%d", &n, &L); 39 for(int i = 1; i <= n; i++) { 40 scanf("%d%lld", &st[i][0], &sum[i]); 41 sum[i] += sum[i - 1]; 42 f[i] = 1ll << 60; 43 } 44 45 STinit(); 46 st[0][0] = 0x7f7f7f7f; 47 to[0] = -1; 48 49 int pos = 0; 50 for(int i = 1; i <= n; i++) { 51 while(st[i][0] >= st[p[top]][0]) { 52 in_stk[p[top]] = 0; 53 top--; 54 } 55 to[i] = p[top]; 56 p[++top] = i; 57 in_stk[i] = 1; 58 59 Q.push(mp(-1 * (f[to[i]] + getmax(to[i] + 1, i)), i)); 60 61 62 while(sum[i] - sum[pos] > L) { 63 pos++; 64 } 65 while((!Q.empty()) && (to[Q.top().second] < pos || (!in_stk[Q.top().second]))) { 66 Q.pop(); 67 } 68 if(!Q.empty()) { 69 f[i] = -1 * Q.top().first; 70 } 71 f[i] = std::min(f[i], f[pos] + getmax(pos + 1, i)); 72 } 73 /*for(int i = 1; i <= n; i++) { 74 printf("%lld ", f[i]); 75 }*/ 76 printf("%lld", f[n]); 77 return 0; 78 }
可是還有不少能夠改進的地方。
好比to這個數組能否捨去?ST表能否捨去?我是懶得優化了T_T