题目大意
将a[1..n]a[1..n]a[1..n]划分成若干个区间,每段区间[i,j][i , j][i,j]的代价为(c[i]+c[i+1]+c[i+2]+…+c[j])2+m(c[i] + c[i + 1] + c[i + 2] + … + c[j]) ^ 2 + m(c[i]+c[i+1]+c[i+2]+…+c[j])2+m。其中mmm为常数。
求最小的总代价和。
a[i]>0a[i] > 0a[i]>0
n≥5∗104n ≥ 5 * 10 ^ 4n≥5∗104
思路
首先,这是个很明显的划分类DPDPDP。
直接上状态:f[i]f[i]f[i]表示前i个数的最小代价。
易得f[i]=minf[j]+(s[i]−s[j])2+mf[i] = min{f[j] + (s[i] - s[j]) ^ 2 + m}f[i]=minf[j]+(s[i]−s[j])2+m
初值:f[0]=0f[0]=0f[0]=0
终值:f[n]f[n]f[n]
-----------华------------丽------------丽------------的------------分------------割------------线-----------
然后,我们发现,这是个非常经典的1D/1D1D/1D1D/1D动态规划
显然是无法通过5∗1045*10^45∗104级别的数据。
考虑优化,对于一道一维状态的DPDPDP题,优化方向只有一个——转移。
如何减少重复或不必要的枚举呢?
设1<j1<j2<i1 < j_1 < j_2 < i1<j1<j2<i
则对于iii的决策,j2j_2j2比j1j_1j1优等价于满足
f[j2]+(s[i]−s[j2])2+m<f[j1]+(s[i]−s[j1])2+mf[j2] + (s[i] - s[j2]) ^ 2 + m < f[j1] + (s[i] - s[j1]) ^ 2 + mf[j2]+(s[i]−s[j2])2+m<f[j1]+(s[i]−s[j1])2+m
简单的打开并整理过后就得到了
(f[j2]−f[j1])+(s[j2]2−s[j1]2)(f[j_2]-f[j_1]) + (s[j_2] ^ 2 - s[j_1] ^ 2)(f[j2]−f[j1])+(s[j2]2−s[j1]2)
--------------------------------------------------- <s[i]< s[i]<s[i]
(s[j2]−s[j1])∗2(s[j2] - s[j1]) * 2(s[j2]−s[j1])∗2
设不等式左边=g(j1,j2)=g(j_1 , j_2)=g(j1,j2)
先考虑用代数
若g(j1,j2)>g(j2,j3)g(j_1 , j_2) > g(j_2 , j_3)g(j1,j2)>g(j2,j3)则由上面的推论可以知道对于i的决策,j1j_1j1比j2j_2j2优,j3j_3j3比j2j_2j2优,即j2j_2j2永远不可能成为最优的转移
又因为a[i]>0a[i] > 0a[i]>0,即s[i]<s[i+1]<…<s[n]s[i] < s[i + 1] < … < s[n]s[i]<s[i+1]<…<s[n]
则j2j_2j2永远不可能成为今后的最优转移
因此,我们可以通过不转移形如g(j1,j2)>g(j2,j3)g(j_1,j_2)>g(j_2,j_3)g(j1,j2)>g(j2,j3)的j2j_2j2
换言之,我们要转移的应该是满足g(j1,j2)≤g(j2,j3)≤…g(j_1,j_2)≤g(j_2,j_3)≤ …g(j1,j2)≤g(j2,j3)≤…
用一个单调队列即可完成维护,每次在队首取值即可
队列中存的是决策点的下标,以队列中相邻两元素构成的g()g()g()为关键字单调递增
以 g(j1,j2)≤g(j2,j3)g(j_1,j_2)≤g(j_2,j_3)g(j1,j2)≤g(j2,j3)为例
①当s[i]≤g(j1,j2)≤g(j2,j3)s[i]≤g(j_1,j_2)≤g(j_2,j_3)s[i]≤g(j1,j2)≤g(j2,j3)时,j1j_1j1比j2j_2j2优,j2j_2j2比j3j_3j3优 取j1j_1j1
②当g(j1,j2)≤s[i]≤g(j2,j3)g(j_1,j_2)≤s[i]≤g(j_2,j_3)g(j1,j2)≤s[i]≤g(j2,j3)时,j2j_2j2比j1j_1j1优,j2j_2j2比j3j_3j3优 弹出j1j_1j1取j2j_2j2
③当g(j1,j2)≤g(j2,j3)≤s[i]g(j_1,j_2)≤g(j_2,j_3)≤s[i]g(j1,j2)≤g(j2,j3)≤s[i]时,j3j_3j3比j2j_2j2优,j2j_2j2比j1j_1j1优 弹出j2j_2j2取j3j_3j3
然后再考虑用较为简便的“数形结合”
发现这恰好与上面的代数一一对应,将g看成一条直线,将s[i]也看成一条直线
分别讨论当s[i]s[i]s[i]的斜率①x<j1j2x < j_1j_2x<j1j2②j1j2<x<j2j3j_1j_2 < x < j_2j_3j1j2<x<j2j3 ③x>j2j3x > j_2j_3x>j2j3
最终可得与上文相同结论
代码
#include<cstdio>
using namespace std;
const int maxn = 500005;
long long s[maxn] , f[maxn];
int a[maxn] , q[maxn];
inline int read()
{
char ch = getchar();
while(ch < '0' || ch > '9') ch = getchar();
int x = 0;
while(ch >= '0' && ch <= '9') x = x * 10 + ch - 48 , ch = getchar();
return x;
}
long long sqr(long long x)
{
return x * x;
}
bool cmp1(int j2 , int j1 , int k)
{
return (f[j2] - f[j1] + sqr(s[j2]) - sqr(s[j1])) <= k * 2 * (s[j2] - s[j1]);
}
bool cmp2(int j3 , int j2 , int j1)
{
return (f[j3] - f[j2] + sqr(s[j3]) - sqr(s[j2])) * (s[j2] - s[j1]) <= (f[j2] - f[j1] + sqr(s[j2]) - sqr(s[j1])) * (s[j3] - s[j2]);
}
int main()
{
int n = read() , m = read();
s[0] = 0;
for(int i = 1;i <= n;i++) a[i] = read() , s[i] = s[i - 1] + a[i];
int head = 1 , tail = 1;
q[1] = 0;
f[0] = 0;
for(int i = 1;i <= n;i++)
{
while(head < tail && cmp1(q[head + 1] , q[head] , s[i])) head++;
f[i] = f[q[head]] + (s[i] - s[q[head]]) * (s[i] - s[q[head]]) + m;
while(head < tail && cmp2(i , q[tail] , q[tail - 1])) tail--;
q[++tail] = i;
}
printf("%lld\n",f[n]);
return 0;
}