嘟嘟嘟
複習一下斜率優化,感覺已經忘得差不多了……
這題切入點在與答案跟切的順序無關。
證明就是假如有三段權值分別為\(x, y, z\),那麼這兩刀不管按什麼順序切,得到的結果都是\(xy + xz + yz\)。
然後就可以dp。
令\(dp[i][j]\)表示前\(i\)個數切\(j\)刀的最大得分,於是就有\(dp[i][j] = max\\)。
觀察這個式子,發現\(j\)只能從\(j - 1\)轉移過來,那索性把\(j\)換到第一維,然後就可以滾動陣列省去第一維了,即\(f[i] = max \\)。
看到乘積,似乎能想到斜率優化。於是假設\(t_2 > t_1\),且\(t_2\)的決策比\(t_1\)優,這時候\(t_1\)就可以扔掉了。那麼就有
\[\begin
g[t_2] + s[t_2] * (s[i] - s[t_2]) &\geqslant g[t_1] + s[t_1] * (s[i] - s[t_1]) \\
\frac &\leqslant s[i]
\end\]
於是我們用單調佇列維護乙個下凸殼就好啦。
坑點在於\(s[t_1] = s[t_2]\),這時候斜率直接返回\(-inf\),把\(t_1\)扔出去。
彩蛋:某谷第12個點卡精度,然後我發現乘以\(1.0\)比強制型別轉換成double精度要高……
#include#include#include#include#include#include#include#include#include#includeusing namespace std;
#define enter puts("")
#define space putchar(' ')
#define mem(a, x) memset(a, x, sizeof(a))
#define in inline
typedef long long ll;
typedef double db;
const ll inf = 1e18;
const db eps = 1e-8;
const int maxn = 2e5 + 5;
const int maxm = 205;
inline ll read()
inline void write(ll x)
int n, k;
ll a[maxn];
ll sum[maxn], f[maxn], g[maxn];
int q[maxn], pre[maxn][maxm];
in db slope(int i, int j)
int main()
memcpy(g, f, sizeof(f));
}write(f[n]), enter;
for(int j = n, i = k; i; --i) j = pre[j][i], write(j), space; enter;
return 0;
}
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 序列分割
題目鏈結 你正在玩乙個關於長度為 n 的非負整數序列的遊戲。這個遊戲中你需要把序列分成 k 1 個非空的塊。為了得到 k 1塊,你需要重複下面的操作 k 次 選擇乙個有超過乙個元素的塊 初始時你只有一塊,即整個序列 選擇兩個相鄰元素把這個塊從中間分開,得到兩個非空的塊。每次操作後你將獲得那兩個新產生...