APIO2014 序列分割 斜率優化DP

2022-04-30 07:09:09 字數 1523 閱讀 1056

[apio2014]序列分割

題目大意:

你正在玩乙個關於長度為\(n\)的非負整數序列的遊戲。這個遊戲中你需要把序列分成\(k+1\)個非空的塊。為了得到\(k+1\)塊,你需要重複下面的操作\(k\)次:

選擇乙個有超過乙個元素的塊(初始時你只有一塊,即整個序列)

選擇兩個相鄰元素把這個塊從中間分開,得到兩個非空的塊。

每次操作後你將獲得那兩個新產生的塊的元素和的乘積的分數。你想要最大化最後的總得分。

\(n<=10^,k<=200\)

首先劃分完\(k\)塊後,發現非常像線性dp模型

自然地想,是不是分數跟划的順序無關?

可以證明是的(歸納法)

那麼,設

\(dp(i,j)\)表示列舉到了\(i\),第\(1...i\)切了幾刀的最大收益。

有\(dp(i,j)=max(dp(k,j-1)+sum[k]*(sum[i]-sum[k]))(1<=k<=i-1)\)

那麼展開式子,化為斜率優化的式子:

\(-dp(k,j-1)=sum[k]*sum[i]-sum[k]^-dp(i,j)\)

其中\(k\)為\(sum[k]\),單調遞增

其中\(x\)為\(sum[i]\),單調遞增

要使\(dp(i,j)\)最大,因此維護下凸包,那麼可以使用單調佇列

空間滾一下就好

空間複雜度:\(o(n)\)(忽略記錄決策點)

時間複雜度:\(o(nk)\)

注:被巨集定義坑了很久。。。

#include#include

#define sid 100050

#define dd double

#define ll long long

#define ri register int

using

namespace

std;

#define getchar() *s ++

char rr[30000005], *s =rr;

inline

intread()

while(c >= '

0' && c <= '9'

)

return p *w;

}ll sum[sid], dp[

2][sid];

int lst[205

][sid], q[sid], n, k;

bool now = 0, pre = 1

;#define x(g) sum[(g)]

#define y(g) (sum[(g)]*sum[(g)]-dp[pre][(g)])inline dd s(

int i, int

j) int

main()

}printf(

"%lld\n

",dp[now][n]);

int e =n;

for(ri i = k; i >= 1; i --)

return0;

}

序列分割

APIO 2014 序列分割

題目鏈結 演算法 首先 我們發現將一段序列切成若干段所獲得的收益與順序無關 於是我們可以用fi,j表示切i次 前j個數的最大收益 令sumi表示ai的字首和 顯然 fi,j max 斜率優化即可 此題記憶體限制較緊 可以使用滾動陣列優化空間複雜度 時間複雜度 o nk includeusing na...

APIO2014 序列分割

將乙個長度為 n 的序列分成 k 段,每次分割一段長度 ge 2 的序列,得分為兩邊序列元素和的乘積,求最大得分 2 leq n leq100000,1 leq k leq min 0 a i 10 4 我們發現一對元素 i,j 產生貢獻 a i a j 的條件是分割後元素不在同一段裡 於是我們知道...

APIO2014 序列分割

嘟嘟嘟 複習一下斜率優化,感覺已經忘得差不多了 這題切入點在與答案跟切的順序無關。證明就是假如有三段權值分別為 x,y,z 那麼這兩刀不管按什麼順序切,得到的結果都是 xy xz yz 然後就可以dp。令 dp i j 表示前 i 個數切 j 刀的最大得分,於是就有 dp i j max 觀察這個式...