花瓶 题解

题面

于是进入正题
这个题看起来非常像"最大子序和"问题,不过又不一样
考虑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;
}
posted @ 2022-06-06 20:00  2K22  阅读(41)  评论(1)    收藏  举报