here
這題竟然卡常 \(+\) 卡精度,我 \(……\)
令 \(f[i][k]\) 表示前 \(i\) 個元素分了 \(k\) 次的最大值,我們可以得出轉移方程(\(sum\)為字首和陣列):
\[f[i][k] = max\
\]這乙個轉移樸素來算是 \(o(n^2k)\) 的複雜度,我們不妨把式子拆開:
\[f[i][k] = f[j][k-1] + sum[j] * (sum[i] - sum[j])\\
= f[j][k-1] + sum[i]*sum[j] - sum[j] ^ 2\\
f[i][k] - sum[i] * sum[j] = f[j][k-1] - sum[j]^2\]
將 \(f[i][k]\) 看作 \(y\) 上截距, \(sum[j]\) 看作自變數, \(f[j][k-1] - sum[j]^2\) 看作因變數,\(-sum[i]\) 則為斜率,由於\(-sum[i]\) 單調遞減,我們可以考慮斜率優化,維護乙個上凸包,複雜度為 \(o(nk)\),空間的話,\(f\) 陣列可以滾動,但是我太懶了,所以沒滾\(……\)
由於卡常,我開了 \(o2\) (我太菜了)
有一點要注意的就是計算斜率的時候分母有可能為 \(0\),我們特判一下
#includeusing namespace std;
typedef long long ll;
typedef long double d;
const int n = 100010;
const int k = 220;
ll f[n][k], a[n], sum[n], q[n], road[n][k], n, kmax;
d x(int j)
d y(int j, int k)
d slope(int i, int j, int k)
void print(int i, int k)
int main()
}printf("%lld\n", f[n][kmax]);
print(n, kmax);
return 0;
}
注意精度??
注意分母為 \(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 序列分割
嘟嘟嘟 複習一下斜率優化,感覺已經忘得差不多了 這題切入點在與答案跟切的順序無關。證明就是假如有三段權值分別為 x,y,z 那麼這兩刀不管按什麼順序切,得到的結果都是 xy xz yz 然後就可以dp。令 dp i j 表示前 i 個數切 j 刀的最大得分,於是就有 dp i j max 觀察這個式...