最值相關問題我們有常用的 最值分治/笛卡爾樹/單調棧 的經典做法,即處理出每個點作為最值能覆蓋的區間的左端點/右端點。
注意一個關鍵點:在笛卡爾樹上,任意兩個點在原序列上最小值的下標,為兩點在原樹上的 LCA。
因為笛卡爾樹是 BST,所以子樹內的下標連續,又因為笛卡爾樹是堆,所以根節點一定是最小值。
知道了這點之後,我們考慮列舉 LCA,並在 LCA 處統計貢獻。我們有 \(f_{u, j}\) 表示在 \(u\) 的子樹內選取了 \(j\) 個位置的最大貢獻。
轉移考慮樹形揹包,根據經典結論複雜度為 \(O(n^2)\)。
// 如果命運對你緘默, 那就活給他看。
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast", "inline", "-ffast-math")
#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
// #define int LL
const int maxn = 4010;
int m, a[maxn];
int n;
LL f[maxn][maxn];
namespace tr {
int s[maxn][2];
int st[maxn], t = 0;
inline void build() {
for(int i = 1; i <= n; ++ i) {
int k = t;
while(k && a[st[k]] > a[i]) k -- ;
if(k) s[st[k]][1] = i;
if(k < t) s[i][0] = st[k + 1];
st[t = (++ k)] = i;
}
}
int sz[maxn];
LL g[maxn];
inline LL cmx(LL& x, LL y) {
if(y > x) x = y; return x;
}
inline void solve(int u) {
sz[u] = 1;
f[u][1] = 1LL * m * a[u] - a[u];
for(int v : {s[u][0], s[u][1]}) {
if(!v) continue ;
solve(v);
for(int j = 0; j <= sz[u] + sz[v]; ++ j) g[j] = f[u][j];
for(int j = 0; j <= sz[u]; ++ j) {
for(int k = 0; k <= sz[v]; ++ k) {
cmx(g[j + k], (LL)f[u][j] + f[v][k] - 2LL * j * k * a[u]);
}
}
sz[u] += sz[v];
for(int j = 0; j <= sz[u]; ++ j) f[u][j] = g[j];
}
}
}
signed main() {
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
ios :: sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> m;
for(int i = 1; i <= n; ++ i) cin >> a[i];
tr :: build();
tr :: solve(tr :: st[1]);
cout << f[tr :: st[1]][m] << '\n';
return 0;
}