[APIO2010] 特别行动队 题解

前言

  • 第一个不看题解自己A的紫,故写题解qwq
  • msjing on 26.7.25:不是这才俩月降蓝了

solve

  • 先写出朴素的转移方程
    • \(f_i=f_j+get(s_i-s_j)\),其中get为\(ax^2+bx+c\)
  • code
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
constexpr int maxn=1e6+10;
long long n;
long long a,b,c;
long long x[maxn];
long long s[maxn];
long long f[maxn];
long long get(int x) {return a*x*x+b*x+c;}
int main()
{
//	freopen("P3628_3.in","r",stdin);
	memset(f,128,sizeof(f));
	f[0]=0;
	cin >> n >> a >> b >> c;
	for (long long i=1;i<=n;i++) cin >> x[i];
	for (long long i=1;i<=n;i++) s[i]=s[i-1]+x[i];
	for (long long i=1;i<=n;i++)
	{
		for (long long j=0;j<i;j++)
		{
			f[i]=max(f[i],f[j]+get(s[i]-s[j]));
		}
	}
	cout << f[n] << endl;
	return 0;
}
  • 预估50实际50
  • 考虑优化
  • 拆开此转移方程,有:\(f_i=f_j+as_j^2-bs_j-2as_is_j+as_i^2+bs_i+c\)
  • 观察发现,转移与i,j有关
  • 把外层i看成定值,发现是一个线性规划问题,要维护一个上凸壳,采用斜率优化
  • \(y=f_j+as_j^2-bs_j\)\(k=2as_i\)\(x=s_j\)
  • k单调,用单调队列维护队头队尾,均摊O(1),可以通过

code

#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
constexpr int maxn=1e6+10;
long long n;
long long a,b,c;
long long x[maxn];
long long s[maxn];
long long f[maxn];
long long q[maxn],l=1,r=1;
long long get(long long x) {return a*x*x+b*x+c;}
long long gt(long long j) {return f[j]+a*s[j]*s[j]-b*s[j];}
int main()
{
//	freopen("P3628_3.in","r",stdin);
	cin >> n >> a >> b >> c;
	for (long long i=1;i<=n;i++) cin >> x[i];
	for (long long i=1;i<=n;i++) s[i]=s[i-1]+x[i];
	f[l]=0;
	for (long long i=1;i<=n;i++)
	{
		while (l<r && gt(q[l+1])-gt(q[l])>=2*a*s[i]*(s[q[l+1]]-s[q[l]])) l++;
		f[i]=f[q[l]]+get(s[i]-s[q[l]]);
		while (l<r && (gt(q[r])-gt(q[r-1]))*(s[i]-s[q[r]])<=(gt(i)-gt(q[r]))*(s[q[r]]-s[q[r-1]])) r--;
		q[++r]=i;
	}
	cout << f[n] << endl;
	return 0;
}

鸣谢

wang7979
LZYXT

posted @ 2026-05-28 15:42  msjing  阅读(16)  评论(0)    收藏  举报