花瓶 题解
于是进入正题
这个题看起来非常像"最大子序和"问题,不过又不一样
考虑DP
赛时想到的一种手法是枚举段数,不过还需要维护前一段信息比较难搞
于是采用别的题见过的手法,枚举每一段的区间左右端点
这样就不用维护两个相关区间的左右端点信息了
然后就是推状态转移方程:
\(f[i][j]=max(f[i][j],f[j][k]+(sum[i]-sum[j])*(sum[j]-sum[k])\)
其中:\(0\leqslant k<j<i\)
\(sum\)表示前缀和
阶段\(i\)表示当前的右端点
状态\(j\)表示当前的左端点,也是上一个的右端点
决策\(k\)表示上一个区间的左端点
然后我们就会发现:
这是\(O(n^3)\)的,最多给到\(50pts\)部分分
考虑DP优化
如果只用单调队列维护\(k\)时间复杂度不会降,所以考虑斜率优化
考虑两个决策点\(a\),\(b\):
\(f[i][j]\\=max(f[i][j],f[j][a]+(sum[i]-sum[j])*(sum[j]-sum[a])
\\=f[j][a]+sum[i]*sum[j]-sum[j]^2-sum[i]*sum[a]+sum[j]*sum[a]\)
\(f[i][j]=f[j][b]+sum[i]*sum[j]-sum[j]^2-sum[i]*sum[b]+sum[j]*sum[b]\)
\(a\)优于\(b\)时:
\(f[j][a]+(sum[j]-sum[i])*sum[a]>f[j][b]+(sum[j]-sum[i])*sum[b]\)
假定\(sum[a]>sum[b]\)
于是:
\(\frac{f[j][a]-f[j][b]}{sum[a]-sum[b]}>sum[i]-sum[j]\)
这个就是维护决策单调性的式子
截距更大就上凸壳走一波
不过:
\(subtask4(30pts):\)无特殊限制
这种情况下就不能直接搞,因为有负数,横坐标不单调
还要\(O(n^2)?\)
std::sort强行使横坐标单调即可
打上时间戳判断该不该枚举即可
CODE:
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
const int o=5050;
int n,a[o],f[o][o],ans;
int l,r,q[o],sum[o],id;
struct node{
int id;
int qz;
}s[o];
double slope(int j,int x,int y){
if(sum[x]==sum[y]){
return 1e15;
}
return 1.0*(f[j][x]-f[j][y])/(1.0*(sum[x]-sum[y]));
}
bool cmp(node s,node t){
if(s.qz==t.qz){
return s.id<t.id;
}
return s.qz<t.qz;
}
void in(){
scanf("%lld",&n);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
s[i].qz=s[i-1].qz+a[i];
sum[i]=s[i].qz;
s[i].id=i;
}
sort(s+1,s+n+1,cmp);
}
void work(){
for(int j=1;j<=n;j++){
l=1,r=0;
for(int i=1;i<=n;i++){
id=s[i].id;
if(id>=j){
continue;
}
while(l<r&&slope(j,q[r],q[r-1])<=slope(j,id,q[r])){
r--;
}
q[++r]=id;
}
for(int i=n;i>=1;i--){
id=s[i].id;
if(id<=j){
continue;
}
while(l<r&&slope(j,q[l+1],q[l])>=(sum[id]-sum[j])){
l++;
}
f[id][j]=max((f[j][q[l]])+(sum[id]-sum[j])*(sum[j]-sum[q[l]]),(sum[id]-sum[j])*sum[j]);
}
}
for(int i=1;i<=n;i++){
ans=max(f[n][i],ans);
}
}
void out(){
cout<<ans;
}
signed main(){
freopen("d.in","r",stdin);
freopen("d.out","w",stdout);
in();
work();
out();
return 0;
}

浙公网安备 33010602011771号