【JZOJ2318】【BZOJ1911】【APIO2010】特别行动队

版权声明:欢迎dalao指出错误暴踩本SD,蒟蒻写的博客转载也要吱一声 https://blog.csdn.net/enjoy_pascal/article/details/89044917

description

你有一支由n名预备役士兵组成的部队,士兵从1到n编号,要将他们拆分成若干特别行动队调入战场。出于默契的考虑,同一支特别行动队中队员的编号应该连续,即为形如(i, i + 1, …, i + k)的序列。 编号为i的士兵的初始战斗力为xi ,一支特别行动队的初始战斗力x为队内士兵初始战斗力之和,即x = xi + xi+1 + … + xi+k。 通过长期的观察,你总结出一支特别行动队的初始战斗力x将按如下经验公式修正为x’:x’ = ax2 + bx + c,其中a, b, c是已知的系数(a < 0)。 作为部队统帅,现在你要为这支部队进行编队,使得所有特别行动队修正后战斗力之和最大。试求出这个最大和。 例如,你有4名士兵,x1 = 2, x2 = 2, x3 = 3, x4 = 4。经验公式中的参数为a = –1, b = 10, c = –20。此时,最佳方案是将士兵组成3个特别行动队:第一队包含士兵1和士兵2,第二队包含士兵3,第三队包含士兵4。特别行动队的初始战斗力分别为4, 3, 4,修正后的战斗力分别为4, 1, 4。修正后的战斗力和为9,没有其它方案能使修正后的战斗力和更大。


analysis

  • 达成成就一天两斜率优化

  • f [ i ] f[i] 表示到第 i i 位的最小值,那么 O ( n 2 ) O(n^2) D P DP 方程很好写

f [ i ] = m i n ( f [ j ] + a ( p r e [ i ] p r e [ j ] ) 2 + b ( p r e [ i ] p r e [ j ] ) + c ) f[i]=min(f[j]+a(pre[i]-pre[j])^2+b(pre[i]-pre[j])+c)

  • 对于 k &gt; j k&gt;j k k j j 优则有

f [ k ] + a ( p r e [ i ] p r e [ k ] ) 2 + b ( p r e [ i ] p r e [ k ] ) + c f [ j ] + a ( p r e [ i ] p r e [ j ] ) 2 + b ( p r e [ i ] p r e [ j ] ) + c f[k]+a(pre[i]-pre[k])^2+b(pre[i]-pre[k])+c≥f[j]+a(pre[i]-pre[j])^2+b(pre[i]-pre[j])+c

  • 拆项移项得

f [ k ] f [ j ] a ( p r e [ i ] p r e [ j ] ) 2 a ( p r e [ i ] p r e [ k ] ) 2 + b ( p r e [ i ] p r e [ j ] ) b ( p r e [ i ] p r e [ k ] ) f[k]-f[j]≥a(pre[i]-pre[j])^2-a(pre[i]-pre[k])^2+b(pre[i]-pre[j])-b(pre[i]-pre[k])

  • 再移

f [ k ] f [ j ] a ( p r e [ i ] 2 2 p r e [ i ] p r e [ j ] + p r e [ j ] 2 ) a ( p r e [ i ] 2 2 p r e [ i ] p r e [ j ] + p r e [ k ] 2 ) + b ( p r e [ k ] p r e [ j ] ) f[k]-f[j]≥a(pre[i]^2-2pre[i]pre[j]+pre[j]^2)-a(pre[i]^2-2pre[i]pre[j]+pre[k]^2)+b(pre[k]-pre[j])

f [ k ] + a p r e [ k ] 2 f [ j ] a p r e [ j ] 2 b ( p r e [ k ] p r e [ j ] ) a p r e [ i ] ( 2 p r e [ k ] 2 p r e [ j ] ) f[k]+apre[k]^2-f[j]-apre[j]^2-b(pre[k]-pre[j])≥apre[i](2pre[k]-2pre[j])

  • 最后

f [ k ] + a p r e [ k ] 2 f [ j ] a p r e [ j ] 2 b ( p r e [ k ] p r e [ j ] ) 2 a ( p r e [ k ] p r e [ j ] ) p r e [ i ] {{f[k]+apre[k]^2-f[j]-apre[j]^2-b(pre[k]-pre[j])}\over{2a(pre[k]-pre[j])}}≥pre[i]

  • 然后按套路上单调队列斜率优化维护上凸壳就可以了

  • 注意先删去较劣的队头后删去较劣的队尾

  • 还有这题删劣决策点的符号很奇怪,我也不太懂

  • 你听说什么是如出一辙吗,推荐试下找不同


code

#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define MAXN 1000005
#define mod 1000000007
#define ll long long
#define reg register ll
#define fo(i,a,b) for (reg i=a;i<=b;++i)
#define fd(i,a,b) for (reg i=a;i>=b;--i)
#define O3 __attribute__((optimize("-O3")))

using namespace std;

ll a[MAXN],f[MAXN],pre[MAXN],que[MAXN];
ll n,A,B,C;

O3 inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while (ch<'0' || '9'<ch){if (ch=='-')f=-1;ch=getchar();}
	while ('0'<=ch && ch<='9')x=x*10+ch-'0',ch=getchar();
	return x*f;
}
O3 inline ll sqr(ll x)
{
	return x*x;
}
O3 inline double slope(ll x,ll y)
{
	return 1.0*(f[x]+A*sqr(pre[x])-f[y]-A*sqr(pre[y])-B*(pre[x]-pre[y]))/(2.0*A*(pre[x]-pre[y]));
}
O3 int main()
{
	//freopen("T3.in","r",stdin);
	n=read(),A=read(),B=read(),C=read();
	fo(i,1,n)pre[i]=pre[i-1]+(a[i]=read());
	ll head=1,tail=1;f[0]=que[1]=0;
	fo(i,1,n)
	{
		while (head<tail && slope(que[head],que[head+1])<=pre[i])++head;
		f[i]=f[que[head]]+A*sqr(pre[i]-pre[que[head]])+B*(pre[i]-pre[que[head]])+C;
		while (head<tail && slope(que[tail-1],que[tail])>=slope(que[tail],i))--tail;
		que[++tail]=i;
	}
	printf("%lld\n",f[n]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/enjoy_pascal/article/details/89044917