题解 P3628 【[APIO2010]特别行动队】

题目链接:Link

Problem

Solution

不难写出方程:

\[f(i)=\min\limits_{0 \le j < i } \{ f(j)+a(s_i-s_j)^2+b(s_i-s_j)+c \} \]

去掉min函数,展开得:

\[f(i)=f(j)+a*s_i^2 - a*s_i*s_j + a*s_j^2 + b*s_i - b*s_j + c \]

将仅与j有关的项、乘积项、和其余的分开,得:

\[f(j)+a*s_j^2-b*s_j=2*a*s_i*s_j + f(i) - a*s_i^2 - b* s_i - c \]

则在这个线性规划模型中,对于每个决策i,决策点位于 $ ( s_i , f(i) + a * s_i^2 - b * s_i ) $ 。
对于每个要求的 $ f(i) $ ,直线斜率为 $ 2 * a * s_i * s_j $ ,截距为 $ f(i) - a * s_i^2 - b * s_i - c $ 。
整个过程中有如下性质:

  • $ s_j $ 单调增
  • 直线斜率单调减

故可用单调队列维护这个斜率优化过程。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn=1000005;
int n,Q[maxn],L,R;
LL a,b,c,v[maxn],s[maxn],X[maxn],Y[maxn],f[maxn];
int main()
{
	#ifdef local
	freopen("pro.in","r",stdin);
	#endif
	scanf("%d%lld%lld%lld",&n,&a,&b,&c);
	for(int i=1;i<=n;i++) scanf("%lld",&v[i]);
	for(int i=1;i<=n;i++) s[i]=s[i-1]+v[i];
	Q[L=R=1]=0;
	for(int i=1;i<=n;i++)
	{
		while(L<R&&(Y[Q[L+1]]-Y[Q[L]])>=2*a*s[i]*(X[Q[L+1]]-X[Q[L]])) L++;
		int j=Q[L];
		f[i]=f[j]+a*(s[i]-s[j])*(s[i]-s[j])+b*(s[i]-s[j])+c;
		X[i]=s[i]; Y[i]=f[i]+a*s[i]*s[i]-b*s[i];
		while(L<R&&(Y[Q[R]]-Y[Q[R-1]])*(X[i]-X[Q[R-1]])<=(Y[i]-Y[Q[R-1]])*(X[Q[R]]-X[Q[R-1]])) R--;
		Q[++R]=i;
	}
	printf("%lld\n",f[n]);
	return 0;
}
posted @ 2019-10-07 10:18  happyZYM  阅读(154)  评论(0编辑  收藏  举报