bzoj1911 [Apio2010]特别行动队

1911: [Apio2010]特别行动队


Time Limit:4 SecMemory Limit:64 MB
Submit:5224Solved:2571
[Submit][Status][Discuss]

Description

Input


                                            bzoj1911 [Apio2010]特别行动队

Output


                                            bzoj1911 [Apio2010]特别行动队

Sample Input

4
-1 10 -20
2 2 3 4

Sample Output

9

HINT


                                            bzoj1911 [Apio2010]特别行动队其他综合收益_04" border="0">

分析:dp斜率优化的裸题.
   令f(i)表示分到第i个人的最linux是什么操作系统大值,那么f(i) = max{f(j) + a * (sum_i - sum_j)^2linux系统 + b * (sum系统运维面试题及答案_i - sum_j) + c}.
  把式子展开一下,可以得到:f(i) = max{-2a*sum_i*sum_j + f(j) + a * sumlinux常用命令_j ^ 2 - b * su系统运维工资一般多少m_j + a * sum_i ^ 2 + b * sum_i + c}.
  这其实是把关于i的部分放到了右边,与j有关的放到了左边. 右边这种和j无关的可include用法以认为是常数(一其他应付款下子就能知道.),可以提到括号外面:f(斜率与倾斜角的关系i) = max{-2a * sum_i * sum_j + f(j) + a * sum_j ^ 2 - binclude和including的区别 * sum_j} + a * sum_i^2 + b * sum_i + c.
  可以发现如果j固定了,那么这个式子的取值是和sum_i相关的,可以令sum_i为自变量x,-2a * sum_j为斜率k,f(j)斜率 + a * sum_j系统运维工作内容 ^ 2 - b * sum_j为截距b. 那么f(i) = kx + b + a * sum_i^2 + b * sum_i + c.
  问题的关键就是如何找到一个j,使得kx + b尽量大. 维护一个双端单调队列,里面存的是直线.当枚举到新的点时,先看队首的两条直线,如果第l条在sumlinux是什么操作系统_i的位置的纵坐标≤第l+1条在sum_i的位置的纵坐标.那么第l条就可以丢啦!why? 随着i的增大,sum_i是不降的,那么斜率也是不降的系统运维工程师面试问题及答案,既然纵坐标已经≤了,并且斜率也include和including的区别≤,那么这条直线以后肯定不会带来贡献,丢掉!includes是什么意思
  接着取出队首元素j,j对应的k和b就用来更新f(i).
  然后看队尾的两个元素r-1,r.如果直线r被直线i和直线r-1完全覆盖了,也就是不可能取到最大值,那么就丢掉.怎么判断是否被完全覆盖呢?如果令r-1和i的交点的横坐标为x,纵坐标为y,如果直include和including的区别线r在横坐标为x时纵坐标≤y了,那么就被完全覆盖了,丢掉即可.
  最后加入i.
  这个单调队列的写法和平时习惯的写法有一点点区别:l = 1,r = 0 变成了 l = 0,r = 0; l <= r变成了l < r:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll maxn = 1000010;
ll n,a,b,c,sum[maxn],q[maxn * 2],l,r,f[maxn];
ll K(ll i)
{
return -2 * a * sum[i];
}
ll B(ll i)
{
return f[i] + a * sum[i] * sum[i] - b * sum[i];
}
ll Y(ll i,ll j)
{
return sum[j] * K(i) + B(i);
}
bool check(int y1,int y2,int y3)
{
ll temp1 = (K(y1) - K(y3)) * (B(y2) - B(y1));
ll temp2 = (K(y1) - K(y2)) * (B(y3) - B(y1));
return temp1 <= temp2;
}
int main()
{
scanf("%lld",&n);
scanf("%lld%lld%lld",&a,&b,&c);
for (int i = 1; i <= n; i++)
{
scanf("%lld",&sum[i]);
sum[i] += sum[i - 1];
}
l = 0,r = 0;
for (int i = 1; i <= n; i++)
{
while (l < r && Y(q[l],i) <= Y(q[l + 1],i))
l++;
f[i] = Y(q[l],i) + a * sum[i] * sum[i] + b * sum[i] + c;
while (l < r && check(i,q[r],q[r - 1]))
r--;
q[++r] = i;
}
printf("%lld\n",f[n]);
return 0;
}