Link.
Description..
给定一个序列 \(\{a_i\}\),选出长度为 \(k\) 的子序列 \(\{a_{b_i}\}\),使得下式最大。
\[\sum_{i=1}^mma_{b_i}-\sum_{i=1}^m\sum_{j=1}^mF(b_i,b_j)
\]
定义 \(F(i,j)\) 表示 \([\min(i,j),\max(i,j)]\) 中最小值。
Solution.
式子可以化成
\[\sum_{i=1}^m(m-1)c_i-2\sum_{i=1}^m\sum_{j=i+1}^mf(b_i,b_j)\\
\]
前半段显然直接计算就行了,主要是后半段。
发现有一个 \(\min\),可以考虑按最值分治,分治处理两端然后合并。
考虑一个 \(dp_{id,j}\) 表示 \(id\) 这个区间选 \(j\) 个数的最大价值。
看似总复杂度是 \(O(n^2m)\) 的,但是考虑树上背包复杂度,发现是 \(O(nm)\) 的。
然后就做完了。
本质上就是笛卡尔树两点距离和,可以考虑背包
Coding.
点击查看代码
//是啊,你就是那只鬼了,所以被你碰到以后,就轮到我变成鬼了{{{
#include<bits/stdc++.h>
using namespace std;typedef long long ll;
template<typename T>inline void read(T &x)
{
x=0;char c=getchar(),f=0;
for(;c<48||c>57;c=getchar()) if(!(c^45)) f=1;
for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
f?x=-x:x;
}
template<typename T,typename...L>inline void read(T &x,L&...l) {read(x),read(l...);}//}}}
const int N=4005;int n,m,id;ll a[N],dp[N][N];
inline void upd(ll &a,ll b) {a<b?a=b:a;}
inline int solve(int l,int r)
{
if(l>r) return 0;else if(l==r) return dp[++id][1]=a[l]*(m-1),id;
int wh=l;for(int i=l+1;i<=r;i++) if(a[i]<a[wh]) wh=i;
int ld=solve(l,wh-1),rd=solve(wh+1,r),lz=wh-l,rz=r-wh;id++;
for(int i=0;i<=lz;i++) for(int j=0;j<=rz;j++)
{
upd(dp[id][i+j],dp[ld][i]+dp[rd][j]-2ll*i*j*a[wh]);
upd(dp[id][i+j+1],dp[ld][i]+dp[rd][j]+(m-1)*a[wh]-2ll*(i*j+i+j)*a[wh]);
}return id;
}
int main()
{
read(n,m);for(int i=1;i<=n;i++) read(a[i]);
return printf("%lld\n",dp[solve(1,n)][m]),0;
}