【洛谷P3628】特别行动队

题目

题目链接:https://www.luogu.com.cn/problem/P3628
你有一支由 \(n\) 名预备役士兵组成的部队,士兵从 \(1\)\(n\) 编号,你要将他们拆分成若干特别行动队调入战场。出于默契的考虑,同一支特别行动队中队员的编号应该连续,即为形如 \((i, i + 1, \cdots i + k)\)的序列。所有的队员都应该属于且仅属于一支特别行动队。
编号为 \(i\) 的士兵的初始战斗力为 \(x_i\) ,一支特别行动队的初始战斗力 \(X\) 为队内士兵初始战斗力之和,即 \(X = x_i + x_{i+1} + \cdots + x_{i+k}\)
通过长期的观察,你总结出对于一支初始战斗力为 \(X\) 的特别行动队,其修正战斗力 \(X'= aX^2+bX+c\),其中 \(a,~b,~c\) 是已知的系数(\(a<0\))。 作为部队统帅,现在你要为这支部队进行编队,使得所有特别行动队的修正战斗力之和最大。试求出这个最大和。
\(n\leq 10^6\)

思路

不记得从哪听说这道题不是很模板?但是感觉还是模板啊QWQ。
\(f[i]\) 表示前 \(i\) 个人分好组的最大和。设 \(s[i]=\sum^{i}_{j=1}x[i]\),显然

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

展开,移项后得到

\[f[j]+a·s[j]^2-b·s[j]=2a·s[i]·s[j]+f[i]-a·s[i]^2-b·s[i]+c \]

那么可以看做一条斜率为 \(2a·s[i]\),截距为 \(f[i]-a·s[i]^2-b·s[i]+c\) 的直线,决策点为 \((s[j],f[j]+a·s[j]^2-b·s[j])\) 的平面直角坐标系。
由于 \(a<0\),那么斜率满足单调递减,同时决策点的横坐标单调递增,所以单调队列维护上凸壳即可。
时间复杂度 \(O(n)\)

代码

#include <cctype>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;

const int N=1000010;
ll a,b,c,f[N],s[N],Y[N];
int n,l,r,q[N];

ll read()
{
	ll d=0,f=1; char ch=getchar();
	while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
	while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar();
	return d*f;
}

double slope(int x,int y)
{
	return 1.0*(Y[y]-Y[x])/(s[y]-s[x]);
}

int main()
{
	n=read(); a=read(); b=read(); c=read();
	for (int i=1;i<=n;i++)
		s[i]=s[i-1]+read();
	l=r=1;
	for (int i=1;i<=n;i++)
	{
		for (int x=q[l],y=q[l+1];;x=q[l],y=q[l+1])
			if (l<r && slope(x,y)>=2.0*a*s[i]) l++; 
				else break;
		int j=q[l];
		f[i]=f[j]+a*(s[i]-s[j])*(s[i]-s[j])+b*(s[i]-s[j])+c;
		Y[i]=f[i]+a*s[i]*s[i]-b*s[i];
		for (int x=q[r-1],y=q[r];;x=q[r-1],y=q[r])
			if (l<r && slope(x,y)<=slope(y,i)) r--;
				else break;
		q[++r]=i;
	}
	printf("%lld",f[n]);
	return 0;
}
posted @ 2020-03-25 17:33  stoorz  阅读(...)  评论(...编辑  收藏