日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

斜率优化之凸包优化与李超线段树

發(fā)布時(shí)間:2024/3/12 编程问答 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 斜率优化之凸包优化与李超线段树 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

  • 前言
  • 凸包優(yōu)化
    • 第一步
    • 第二步
    • 最后一步
  • 例一
    • 轉(zhuǎn)移方程
    • 凸包優(yōu)化
    • 代碼
  • 例二
    • 題目大意
    • 轉(zhuǎn)移方程
    • 凸包優(yōu)化
    • 代碼
  • 李超線段樹
    • 思想
      • 插入
      • 查詢
    • 代碼
  • 例三
    • 代碼
  • 例四
    • 轉(zhuǎn)移方程
    • 怎么做
    • 代碼

前言

這種方法比傳統(tǒng)斜率優(yōu)化更快,更準(zhǔn),更狠。

凸包優(yōu)化

一切形如dp[i]=min?/max?{f1(j)?g1(i)+f2(j)}+g2(i)dp[i]=\min/\max\{f_1(j) \cdot g_1(i) + f_2(j)\} + g_2(i)dp[i]=min/max{f1?(j)?g1?(i)+f2?(j)}+g2?(i)的轉(zhuǎn)移方程,都可以凸包優(yōu)化。
其中,fff為關(guān)于jjj的函數(shù),ggg為關(guān)于iii的函數(shù)。
例如dp[i]=min?{?2hj?hi+hj2+dp[j]}+ai+hi2dp[i] = \min\{-2h_j \cdot h_i + {h_j}^2 + dp[j]\} + a_i + {h_i}^2dp[i]=min{?2hj??hi?+hj?2+dp[j]}+ai?+hi?2(這里面,f1(j)=?2hjf_1(j) = -2h_jf1?(j)=?2hj?f2(j)=hj2+dp[j]f_2(j) = {h_j}^2 + dp[j]f2?(j)=hj?2+dp[j]g1(i)=hig_1(i) = h_ig1?(i)=hi?g2(i)=ai+hi2g_2(i) = a_i + {h_i}^2g2?(i)=ai?+hi?2


我們接下來口胡dp[i]=max?{f1(j)?g1(i)+f2(j)}+g2(i)dp[i]=\max\{f_1(j) \cdot g_1(i) + f_2(j)\} + g_2(i)dp[i]=max{f1?(j)?g1?(i)+f2?(j)}+g2?(i)的情況。

很簡單。

第一步

定義一個(gè)關(guān)于g1(i)g_1(i)g1?(i)jjj的二元函數(shù):Lj(g1(i))=f1(j)?g1(i)+f2(j)L_j\left(g_1(i)\right)=f_1(j) \cdot g_1(i) + f_2(j)Lj?(g1?(i))=f1?(j)?g1?(i)+f2?(j)為什么叫LjL_jLj?呢,因?yàn)檫@是一條直線,這條直線的斜率為f1(j)f_1(j)f1?(j),縱截距為f2(j)f_2(j)f2?(j)

第二步

dp[i]=max?{Lj(g1(i))}+g2(i)dp[i]=\max\{L_j(g_1(i))\} + g_2(i)dp[i]=max{Lj?(g1?(i))}+g2?(i)也就是說,我們只需要找直線x=g1(i)x = g_1(i)x=g1?(i)與所有LjL_jLj?的交點(diǎn)中縱坐標(biāo)最大的那個(gè)。

最后一步

用個(gè)李超線段樹即可。
但是,在大多數(shù)題你都會發(fā)現(xiàn),f1f_1f1?g1g_1g1?有單調(diào)性。
否則,用李超線段樹或CDQ或平衡樹什么的即可。

那么我接下來講f1f_1f1?單調(diào)減,g1g_1g1?單調(diào)增的情況吧。
再說一遍,很簡單。(你發(fā)現(xiàn)我們沒有進(jìn)行任何計(jì)算)


現(xiàn)在要計(jì)算dp[i]dp[i]dp[i],則我們可以做到:此時(shí)已經(jīng)按順序把所有Lj(1≤j<i)L_j(1\leq j <i)Lj?(1j<i)放進(jìn)了一個(gè)雙端隊(duì)列QQQ,呈這個(gè)樣子(111LQ[Head]L_{Q[Head]}LQ[Head]?222LQ[Head+1]L_{Q[Head + 1]}LQ[Head+1]?,以此類推):

加粗的地方是這個(gè)直線的“貢獻(xiàn)”,但有些直線沒有貢獻(xiàn),例如下圖中的黑線:

基于歸納的思想,我們可以假設(shè)此時(shí)隊(duì)列中沒有這種線(?)(*)(?),然后在該次DP后維護(hù)這樣一個(gè)雙端隊(duì)列QQQ

一個(gè)顯然的結(jié)論是:由于g1(i)g_1(i)g1?(i)單增,那么如果g1(i)g_1(i)g1?(i)到了這個(gè)地方,藍(lán)線就沒用了:

所以,不斷比較LQ[Head](g1(i))L_{Q[Head]}(g_1(i))LQ[Head]?(g1?(i))LQ[Head+1](g1(i))L_{Q[Head + 1]}(g1(i))LQ[Head+1]?(g1(i)),來看LQ[Head]L_{Q[Head]}LQ[Head]?有沒有存在的必要,類似傳統(tǒng)斜率優(yōu)化。

然后,考慮加入當(dāng)前直線LiL_iLi?(下圖中的黑色),如果是這樣的,那么綠線就沒有用了(333LQ[Tail]L_{Q[Tail]}LQ[Tail]?222LQ[Tail?1]L_{Q[Tail - 1]}LQ[Tail?1]?,以此類推):

這個(gè)問題的刻畫也很好想到:是比較 LiL_iLi?LQ[Tail]L_{Q[Tail]}LQ[Tail]?的交點(diǎn)LQ[Tail]L_{Q[Tail]}LQ[Tail]?LQ[Tail?1]L_{Q[Tail - 1]}LQ[Tail?1]?的交點(diǎn)橫坐標(biāo)。下圖中,若xA<xBx_A<x_BxA?<xB?,那LQ[Tail]L_{Q[Tail]}LQ[Tail]?就沒用了:

于是這樣就能做到(?)(*)(?)了,也是類似于傳統(tǒng)斜率優(yōu)化。


說完了,看例題代碼有驚♂喜。

例一

Kalila and Dimna in the Logging Industry

轉(zhuǎn)移方程

不用看題,直接看轉(zhuǎn)移方程即可:dp[i]=min?1≤j<i{dp[j]+bj?ai}dp[i] = \min\limits_{1 \leq j < i}\{dp[j] + b_j \cdot a_i\}dp[i]=1j<imin?{dp[j]+bj??ai?}其中aia_iai?遞增,bib_ibi?遞減。

凸包優(yōu)化

f1(j)=bjf_1(j)=b_jf1?(j)=bj?g1(i)=aig_1(i)=a_ig1?(i)=ai?f2(j)=dp[j]f_2(j)=dp[j]f2?(j)=dp[j]g2(i)=0g_2(i)=0g2?(i)=0,其中f1f_1f1?單減,g1g_1g1?單增,跟上面講的情況一模一樣。

代碼

#include <algorithm> #include <cstdio> #include <cstring>typedef long long LL;const int MAXN = 100000; const LL INF = 1ll << 60;int N; LL A[MAXN + 5], B[MAXN + 5];LL Dp[MAXN + 5];struct Line {LL k, b;Line() { }Line(LL _k, LL _b) { k = _k, b = _b; }LL Calc(int x) { return k * x + b; } // 算函數(shù)值double Ints(Line other) { // 求兩直線交點(diǎn)的橫坐標(biāo)return (double)(other.b - b) / (k - other.k);} }Q[MAXN + 5];int Head, Tail;int main() {scanf("%d", &N);for (int i = 1; i <= N; i++)scanf("%lld", A + i);for (int i = 1; i <= N; i++)scanf("%lld", B + i);Q[Head = Tail = 1] = Line(B[1], 0); // 邊界注意一下即可for (int i = 2; i <= N; i++) {int x = A[i];while (Tail - Head + 1 >= 2 && Q[Head].Calc(x) >= Q[Head + 1].Calc(x))Head++;Dp[i] = Q[Head].Calc(x); // 找到x=A[i]處的最低點(diǎn)Line cur(B[i], Dp[i]);while (Tail - Head + 1 >= 2 && Q[Tail].Ints(cur) <= Q[Tail].Ints(Q[Tail - 1]))Tail--;Q[++Tail] = cur; // 加入Li}printf("%lld\n", Dp[N]);return 0; }

例二

Hit the Coconuts

題目大意

你想打開zzz個(gè)椰子吃,你的沙比隊(duì)友給你準(zhǔn)備了nnn個(gè)椰子,每個(gè)椰子的堅(jiān)硬♂程度不同,第iii個(gè)椰子的堅(jiān)硬♂程度是aia_iai?,表示它要被敲aia_iai?下才能被打開(不一定要連續(xù)敲)。 你不知道椰子的順序。 請問至少要敲多少下才能打開最少zzz個(gè)椰子。
有必要看一下樣例:

Input 2 2 1 50 55 2 1 40 100 Output 55 80

第一個(gè):抓一個(gè)直接敲55下,不管怎么樣都能敲開;
第二個(gè):抓一個(gè),先敲40下,如果沒開,就拿另一個(gè)敲40下,至少能得到1個(gè)椰子。

轉(zhuǎn)移方程


我都沒看出來是個(gè)DP。


先排個(gè)序,然后先考慮怎么敲開一個(gè)椰子:

記陰影矩形的面積為SiS_iSi?,如果我們想撬開1個(gè)椰子,那敲min?{Si}\min\{S_i\}min{Si?}下就行了,因?yàn)閷τ谌我庖环Nai×(n?i+1)a_i\times(n-i+1)ai?×(n?i+1)下的方案,必定能敲出一個(gè)椰子:先隨便找個(gè)椰子敲aia_iai?下,如果沒打開,就換一個(gè)沒敲過的再敲,重復(fù)此操作,臉再黑也就是把陰影部分倒著敲完,那也能把第iii個(gè)敲開。

接下來考慮,如果我們想敲開兩個(gè)椰子,答案是min?i<j{Si∪Sj}\min\limits_{i<j}\{S_i\cup S_j\}i<jmin?{Si?Sj?}

考慮你是一個(gè)黑人的情況:先敲了SiS_iSi?下才敲開一個(gè)椰子,那你的椰子變成了這樣:

然后,你肯定知道哪些是敲過的,你就在敲過的那些里面敲SjS_jSj?下,就又打開了一個(gè)椰子。


于是問題轉(zhuǎn)變?yōu)樵诰匦卫锩嬲颐娣e最小的,含zzz級的階梯的階梯形(我是倒著來的):dp[i][j]=min?k>j{dp[i?1][k]+aj?(k?j)}dp[i][j] = \min\limits_{k>j}\{dp[i - 1][k] + a_j\cdot(k-j)\}dp[i][j]=k>jmin?{dp[i?1][k]+aj??(k?j)}

凸包優(yōu)化

dp[i][j]=min?k>j{dp[i?1][k]+k?aj}?aj?jdp[i][j] = \min\limits_{k>j}\{dp[i - 1][k] + k\cdot a_j\}-a_j\cdot jdp[i][j]=k>jmin?{dp[i?1][k]+k?aj?}?aj??jf1(k)=kf_1(k)=kf1?(k)=kf2(k)=dp[i?1][k]f_2(k)=dp[i - 1][k]f2?(k)=dp[i?1][k]g1(j)=ajg_1(j) = a_jg1?(j)=aj?g2(j)=aj?jg_2(j) = a_j\cdot jg2?(j)=aj??j,注意iii跟凸包優(yōu)化無關(guān),是j,kj,kj,k參與凸包優(yōu)化。
由于我倒著來的,所以f1f_1f1?單減,g1g_1g1?單減,然后就簡單了。

代碼

#include <algorithm> #include <cstdio> #include <cstring>typedef long long LL;const int MAXN = 1000;int N, Z; LL H[MAXN + 5];LL Dp[MAXN + 5][MAXN + 5];struct Line {LL k, b;Line() { }Line(LL x, LL y) { k = x, b = y; }LL Calc(int x) {return k * x + b;}double Ints(Line other) {return (double)(b - other.b) / (other.k - k);} }Q[MAXN + 5]; int Head, Tail;/* 1 3 2 1 8 10 */int main() {int T; scanf("%d", &T);while (T--) {scanf("%d%d", &N, &Z);for (int i = 1; i <= N; i++)scanf("%lld", &H[i]);std::sort(H + 1, H + 1 + N);for (int i = 1; i <= N; i++)Dp[1][i] = (N - i + 1) * H[i];for (int i = 2; i <= Z; i++) {Q[Head = Tail = 1] = Line(N - i + 2, Dp[i - 1][N - i + 2]);for (int j = N - i + 1; j >= 1; j--) { // 注意邊界int x = H[j];while (Tail - Head + 1 >= 2 && Q[Tail].Calc(x) >= Q[Tail - 1].Calc(x))Tail--;Dp[i][j] = Q[Tail].Calc(x) - H[j] * j;Line cur(j, Dp[i - 1][j]); // 當(dāng)前層是加上一層的直線 通過轉(zhuǎn)移方程就能看出來while (Tail - Head + 1 >= 2 && Q[Tail].Ints(cur) <= Q[Tail].Ints(Q[Tail - 1]))Tail--;Q[++Tail] = cur;}}LL Ans = 1ll << 60;for (int i = 1; i <= N - Z + 1; i++)Ans = std::min(Ans, Dp[Z][i]);printf("%lld\n", Ans);}return 0; }

李超線段樹

如果f1f_1f1?g1g_1g1?沒有單調(diào)性,我們就不能用雙端隊(duì)列維護(hù)了。
李超線段樹的作用很簡單:維護(hù)一些一次函數(shù)(直線 / 線段),支持插入和查詢,查詢時(shí)可以找到當(dāng)前橫坐標(biāo)下最大 / 最小的函數(shù)值
完美解決幾乎所有凸包優(yōu)化。

代碼只有40行。

思想

它每個(gè)區(qū)間記錄的是該區(qū)間中點(diǎn)處的最大函數(shù)值對應(yīng)的函數(shù)MaxiMax_iMaxi?

插入

插入直線curcurcur的過程如下:

  • curcurcur在這個(gè)區(qū)間上完全覆蓋了MaxiMax_iMaxi?:將MaxiMax_iMaxi?變成curcurcur,返回(沒有懶標(biāo)記,不用再改兒子,看查詢的過程就知道了);
  • 如果該區(qū)間中點(diǎn)處Maxi(mid)<cur(mid)Max_i(mid)<cur(mid)Maxi?(mid)<cur(mid),則交換MaxiMax_iMaxi?curcurcur,保證MaxiMax_iMaxi?的意義正確;
  • 現(xiàn)在的curcurcur會對交點(diǎn)所在子樹產(chǎn)生貢獻(xiàn)(下圖中,右子樹的橙色段需要修改),因此遞歸下去:

查詢

比較簡單,遞歸得到下層的答案,跟自己這層比(因此不用插入和查詢都可以不用懶標(biāo)記)即可。

代碼

見例題,有驚♂喜。

例三

[JSOI2008]Blue Mary開公司
這是一道版題。

代碼

#include <algorithm> #include <cstdio> #include <cstring>const int MAXT = 100000; const int MAXX = 50000; const double INF = 1e9;struct LiChao_Tree {#define lch (i << 1)#define rch (i << 1 | 1)struct Line {double k, b;inline double Calc(int x) {return k * x + b;}}Max[MAXT + 5];inline bool Cover(Line Low, Line High, int x) { // 判斷x處Hight否覆蓋了Lowreturn Low.Calc(x - 1) <= High.Calc(x - 1);}void Insert(int i, int l, int r, Line cur) {if (Cover(Max[i], cur, l) && Cover(Max[i], cur, r)) {Max[i] = cur;return;}if (l == r)return;int mid = (l + r) >> 1;if (Cover(Max[i], cur, mid))std::swap(Max[i], cur);if (Cover(Max[i], cur, l))Insert(lch, l, mid, cur);if (Cover(Max[i], cur, r))Insert(rch, mid + 1, r, cur);}double Query(int i, int l, int r, int x) {double tmp = -INF;int mid = (l + r) >> 1;if (x < mid)tmp = Query(lch, l, mid, x);if (x > mid)tmp = Query(rch, mid + 1, r, x);return std::max(tmp, Max[i].Calc(x - 1));} }Tree;int main() {int T, X; scanf("%d", &T);while (T--) {char opt[20];scanf("%s", opt);if (opt[0] == 'P') {LiChao_Tree::Line tmp;scanf("%lf%lf", &tmp.b, &tmp.k);Tree.Insert(1, 1, MAXX, tmp);}else {scanf("%d", &X);printf("%d\n", int(Tree.Query(1, 1, MAXX, X) / 100));}}return 0; }

例四

Jump mission

轉(zhuǎn)移方程

dp[i]=min?j<i且pj<pi{dp[j]+(hi?hj)2}+aidp[i]=\min\limits_{_{j<i\text{且}p_j<p_i}}\{dp[j]+(h_i-h_j)^2\}+a_idp[i]=j<ipj?<pi??min?{dp[j]+(hi??hj?)2}+ai?其中ppp不單調(diào),hhh不單調(diào),aaa不單調(diào)。

怎么做

看到這個(gè)題,什么都不單調(diào),還尼瑪有轉(zhuǎn)移限制???

不可做,溜了。


正解:樹狀數(shù)組套李超樹維護(hù)凸包

樹狀數(shù)組中,每個(gè)結(jié)點(diǎn)是一個(gè)李超樹,維護(hù)對應(yīng)區(qū)間的凸包。查詢的時(shí)候,從pip_ipi?用lowbit減到000,根據(jù)樹狀數(shù)組的性質(zhì),訪問到的恰好就是dp[i]dp[i]dp[i]的所有轉(zhuǎn)移直線,統(tǒng)計(jì)最大的函數(shù)值即可。(其實(shí)樹狀數(shù)組很大的一個(gè)用處就是處理偏序問題,一定程度上可以替代CDQ分治)

代碼

#include <algorithm> #include <cstdio> #include <cstring>typedef long long LL;const int MAXN = 300000; const int MAXL = 600000; const LL INF = 1ll << 60;struct Line {LL k, b;Line() { k = 0, b = INF; }Line(LL _k, LL _b) { k = _k, b = _b; }LL Calc(int x) { return k * x + b; }double Ints(Line other) {return (double)(other.b - b) / (k - other.k);} };struct LiChao_Tree {#define lch (Child[i][0])#define rch (Child[i][1])Line Min[MAXN * 20 + 5];int NodeCnt;int Child[MAXN * 20 + 5][2];inline bool Cover(Line Low, Line High, int x) {return Low.Calc(x) <= High.Calc(x);}void Insert(int &i, int l, int r, Line cur) {if (!i)i = ++NodeCnt;if (Cover(cur, Min[i], l) && Cover(cur, Min[i], r)) {Min[i] = cur;return;}if (l == r)return;int mid = (l + r) >> 1;if (Cover(cur, Min[i], mid))std::swap(Min[i], cur);if (Cover(cur, Min[i], l))Insert(lch, l, mid, cur);if (Cover(cur, Min[i], r))Insert(rch, mid + 1, r, cur);}LL Query(int i, int l, int r, int x) {LL tmp = INF;int mid = (l + r) >> 1;if (x < mid)tmp = Query(lch, l, mid, x);if (x > mid)tmp = Query(rch, mid + 1, r, x);return std::min(tmp, Min[i].Calc(x));}#undef lch#undef rch }Tree;struct BIT {#define lowbit(x) ((x) & (-(x)))int Root[MAXN + 5];void Update(int p, Line l) {for (int i = p; i <= MAXN; i += lowbit(i))Tree.Insert(Root[i], 1, MAXL, l);}LL GetMin(int p, int x) {LL ret = INF;for (int i = p; i > 0 ; i -= lowbit(i))ret = std::min(ret, Tree.Query(Root[i], 1, MAXL, x));return ret;}#undef lowbit }CHT;int N, P[MAXN + 5]; LL A[MAXN + 5], H[MAXN + 5];LL Dp[MAXN + 5];int main() {scanf("%d", &N);for (int i = 1; i <= N; i++)scanf("%d", &P[i]);for (int i = 1; i <= N; i++)scanf("%lld", &A[i]);for (int i = 1; i <= N; i++)scanf("%lld", &H[i]);CHT.Update(P[1], Line(-2 * H[1], A[1] + H[1] * H[1]));for (int i = 2; i <= N; i++) {Dp[i] = CHT.GetMin(P[i], H[i]) + A[i] + H[i] * H[i];CHT.Update(P[i], Line(-2 * H[i], Dp[i] + H[i] * H[i]));}printf("%lld", Dp[N]);return 0; }

總結(jié)

以上是生活随笔為你收集整理的斜率优化之凸包优化与李超线段树的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 欧美三级在线 | 在线免费观看网站入口在哪 | 欧美日韩在线视频观看 | 一二三区精品视频 | 五月婷婷深爱 | 美女视频网址 | wwwyoujizz日本| 高清无码视频直接看 | 欧美黄色三级视频 | 农村妇女愉情三级 | 一区二区免费在线观看 | 久操伊人网 | 麻豆最新 | 欧美日韩综合网 | 成人一区二区电影 | 国产精品福利导航 | 天天操穴 | 一本大道东京热无码aⅴ | 免费午夜影院 | 国产呻吟av | 免费黄色在线网站 | 一级片欧美 | 久久国产日韩欧美 | 欧美夫妇交换xxx | 色吧在线观看 | 九九国产 | 一区三区在线 | 欧美一区二区在线播放 | 91色啪| 91久久一区二区三区 | 欧美不卡一区二区 | 不许穿内裤随时挨c调教h苏绵 | 亚洲av无码专区在线播放中文 | 国产成人短视频在线观看 | 521av在线 | 巨大胸大乳奶电影 | 乱色精品无码一区二区国产盗 | 黄瓜污视频 | 国产黄色a| www.黄色国产 | 中文字幕一区在线播放 | 怡红院成人影院 | 海角社区id:1220.7126,10. | 99草| 日韩深夜视频 | 欧美性生活网 | 黄色xxx| 欧美人和黑人牲交网站上线 | 先锋影音一区二区 | 国产极品一区二区 | 欧美爱爱一区二区 | 欧美三级韩国三级日本三斤在线观看 | av官网| 国产美女精品视频国产 | 自拍偷拍一区 | 美国少妇性做爰 | 久久免费一级片 | 国产精品视频一 | 日av在线播放 | 蜜桃网av | 国产又爽又黄游戏 | 国产精品国产三级国产普通话对白 | 欧美日本韩国 | www国产亚洲精品久久麻豆 | 中文字幕电影av | 亚欧洲精品在线视频免费观看 | yy色综合| 欧美人妖乱大交 | 大屁股一区二区三区 | 日本黄色精品 | 97欧美视频| 中文幕无线码中文字夫妻 | 伊人夜夜 | 麻豆视频黄色 | 国产精品一线二线三线 | 日韩欧美国产另类 | 国产区一区二区三区 | 69精品人人| 亚洲蜜臀av| 中文字幕色站 | 黄色工厂这里只有精品 | av动漫网| av剧情在线 | 狠狠干狠狠操 | 亚洲第一色站 | 狠狠干男人的天堂 | 一卡二卡三卡视频 | 国精产品一区一区三区有限公司杨 | 久久人| 98成人网| 爱爱视频免费网站 | 日本阿v视频在线观看 | 色大师av一区二区三区 | 久久国产精品国产精品 | 香蕉成视频人app下载安装 | 国产日产精品一区二区三区四区 | 青草一区二区 | 成人精品电影 | 亚洲一区二区福利 |