特别行动队

https://loj.ac/problem/10190

题目描述

  有\(n\)个士兵,每个士兵有一定的战斗力,分为若干支特别行动队,每支行动队内的士兵编号连续,令\(x=\sum x_i\),那么这支特别行动队的战斗力为\(ax^2+bx+c\),求最大战斗力。

思路

  考虑用\(f[i]\)表示前\(i\)个人能到达的战斗力,那么方程比较简单,就是把\(k+1\sim j\)分为一组

\[f[i]=min\{f[j]+a(sum[i]-sum[j])^2+b(sum[i]-sum[j])+c\} \]

  我们尝试把这个式子化为与\(j\)相关的一次函数

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

  对于这个式子我们显然可以用斜率优化优化为\(O(N)\)

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=1e6+10;

ll read()
{
	ll res=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){res=(res<<3)+(res<<1)+(ch^48);ch=getchar();}
	return res*w;
}

ll a,b,c,sum[N],f[N],q[N];
ll Y(ll x){return f[x]+a*sum[x]*sum[x];}
ll K(ll x){return 2*a*sum[x]+b;}
ll X(ll x){return sum[x];}
int main()
{
	ll n=read();
	a=read(),b=read(),c=read();
	for(ll i=1;i<=n;i++)
		sum[i]=sum[i-1]+read();
	ll l=0,r=0;
	for(ll i=1;i<=n;i++)
	{
		while(l<r&&(Y(q[l+1])-Y(q[l]))>=K(i)*(X(q[l+1])-X(q[l])))l++;
		ll x=sum[i]-sum[q[l]];
		f[i]=f[q[l]]+a*x*x+b*x+c;
		while(l<r&&(Y(q[r])-Y(q[r-1]))*(X(i)-X(q[r]))<=(Y(i)-Y(q[r]))*(X(q[r])-X(q[r-1])))r--;
		q[++r]=i;
	}
	printf("%lld",f[n]);
}
posted @ 2019-11-13 21:51  fbz  阅读(181)  评论(0编辑  收藏  举报