特别行动队
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]);
}