# 單調隊列 ## [a146. Sliding Window](https://zerojudge.tw/ShowProblem?problemid=a146) ## [Nearest Smaller Values](https://cses.fi/problemset/task/1645) ## [Advertisement](https://cses.fi/problemset/task/1142) ## 有限個數的多重背包問題 # 斜率優化 ## [Monster Game I](https://cses.fi/problemset/task/2084) $$ dp[i]=\min_{j<i} \{ f_j\times s_i+dp[j]\} $$ ### 觀察1 很像直線$y=ax+b$的形式 所以我們可以維護一個直線的集合,每次詢問找將$s_i$帶入這些直線的最小值。 詢問後加入集合 ### 觀察2 ![image](https://hackmd.io/_uploads/r1haOv4rp.png) 對於所有的$j<i$,我們只需要存黑色的就好。 剛好會是一個凸包 ### 觀察3 計算答案 ![image](https://hackmd.io/_uploads/SJP1qYNra.png) 因為詢問單調,如果詢問是藍色,那麼斜率比轉移點大的都可以刪掉了。 ### 觀察4 ![image](https://hackmd.io/_uploads/HkVtNRFBp.png) 將直線加入凸包後,可能會有一些斜率小且**連續**的直線被淘汰。 ### 實作 維護一個直線的deque,斜率遞減。 詢問時: 重複比較斜率最**大**的兩條直線$L_1$、$L_2$,如果$L_2$的答案比$L_1$還要好的話就可以將$L_1$丟掉,反之$L_1$就會是轉移點。 加入時: 對於斜率最**小**的兩條直線$L_1$、$L_2$及加入的直線$L$,有兩種情形: ![image](https://hackmd.io/_uploads/H1WfhAKHa.png =300x200)![image](https://hackmd.io/_uploads/Bklvh0KHp.png =300x200) 若$L$、$L_2$的交點$C$的$x$座標小於$L_1$、$L_2$的交點$D$的$x$座標,則可以把$L_1$丟掉。(這一題斜率都是正的,所以也可以看$y$座標) 即 $$ \begin{cases} C_y=L_{a}C_x+L_b \\C_y=L_{2_a}C_x+L_{2_b} \\D_y=L_{1_a}D_x+L_{1_b} \\D_y=L_{2_a}D_x+L_{2_b} \end{cases} $$ 中,判斷是否有$C_x<D_x$,由上式可知 $$ \begin{cases} C_x=\frac{L_b-L_{2_b}}{L_{2_a}-L_a} \\D_x=\frac{L_{1_b}-L_{2_b}}{L_{2_a}-L_{1_a}} \end{cases} $$ 為了避免浮點數誤差,交叉相乘,所以要檢查 $$ (L_b-L_{2_b})(L_{2_a}-L_{1_a})\leq (L_{2_a}-L_a)(L_{1_b}-L_{2_b}) $$ 是否成立 ### code ```cpp= #include<bits/stdc++.h> #define int long long using namespace std; struct F{ int a,b; F(int _a,int _b):a(_a),b(_b){} int operator()(int x){ return a*x+b; } }; bool check(F l1,F l2,F l){ return (l.b-l2.b)*(l2.a-l1.a)<=(l2.a-l.a)*(l1.b-l2.b); } signed main(){ int n,x; cin>>n>>x; vector<int> s(n+1),f(n+1),dp(n+1,1e18); for(int i=1;i<=n;i++) cin>>s[i]; for(int i=1;i<=n;i++) cin>>f[i]; dp[0]=0; f[0]=x; deque<F> dq; dq.push_back(F(x,0)); for(int i=1;i<=n;i++){ while(dq.size()>=2&&dq[1](s[i])<=dq[0](s[i])){ dq.pop_front(); } dp[i]=dq[0](s[i]); F l(f[i],dp[i]); while(dq.size()>=2&&check(dq.back(),dq[dq.size()-2],l)){ dq.pop_back(); } dq.push_back(l); } cout<<dp[n]<<endl; } ``` ## [Monster Game II](https://cses.fi/problemset/task/2085) 查詢和斜率都不單調的版本 李超線段樹 查詢不單調: 對每個直線存他是最佳答案的區間,二分搜 斜率不單調: 動態凸包