[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 觀察這個式...