题目大意
将 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。其中 m m m为常数。
求最小的总代价和。
a [ i ] > 0 a[i] > 0 a[i]>0
n ≥ 5 ∗ 1 0 4 n ≥ 5 * 10 ^ 4 n≥5∗104
思路
首先,这是个很明显的划分类 D P DP DP。
直接上状态: f [ i ] f[i] f[i]表示前i个数的最小代价。
易得 f [ i ] = m i n f [ j ] + ( s [ i ] − s [ j ] ) 2 + m f[i] = min{f[j] + (s[i] - s[j]) ^ 2 + m} f[i]=minf[j]+(s[i]−s[j])2+m
初值: f [ 0 ] = 0 f[0]=0 f[0]=0
终值: f [ n ] f[n] f[n]
-----------华------------丽------------丽------------的------------分------------割------------线-----------
然后,我们发现,这是个非常经典的 1 D / 1 D 1D/1D 1D/1D动态规划
显然是无法通过 5 ∗ 1 0 4 5*10^4 5∗104级别的数据。
考虑优化,对于一道一维状态的 D P DP DP题,优化方向只有一个——转移。
如何减少重复或不必要的枚举呢?
设 1 < j 1 < j 2 < i 1 < j_1 < j_2 < i 1<j1<j2<i
则对于 i i i的决策, j 2 j_2 j2比 j 1 j_1 j1优等价于满足
f [ j 2 ] + ( s [ i ] − s [ j 2 ] ) 2 + m < f [ j 1 ] + ( s [ i ] − s [ j 1 ] ) 2 + m f[j2] + (s[i] - s[j2]) ^ 2 + m < f[j1] + (s[i] - s[j1]) ^ 2 + m f[j2]+(s[i]−s[j2])2+m<f[j1]+(s[i]−s[j1])2+m
简单的打开并整理过后就得到了
( f [ j 2 ] − f [ j 1 ] ) + ( s [ j 2 ] 2 − s [ j 1 ] 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 [ j 2 ] − s [ j 1 ] ) ∗ 2 (s[j2] - s[j1]) * 2 (s[j2]−s[j1])∗2
设不等式左边 = g ( j 1 , j 2 ) =g(j_1 , j_2) =g(j1,j2)
先考虑用代数
若 g ( j 1 , j 2 ) > g ( j 2 , j 3 ) g(j_1 , j_2) > g(j_2 , j_3) g(j1,j2)>g(j2,j3)则由上面的推论可以知道对于i的决策, j 1 j_1 j1比 j 2 j_2 j2优, j 3 j_3 j3比 j 2 j_2 j2优,即 j 2 j_2 j2永远不可能成为最优的转移
又因为 a [ i ] > 0 a[i] > 0 a[i]>0,即 s [ i ] < s [ i + 1 ] < … < s [ n ] s[i] < s[i + 1] < … < s[n] s[i]<s[i+1]<…<s[n]
则 j 2 j_2 j2永远不可能成为今后的最优转移
因此,我们可以通过不转移形如 g ( j 1 , j 2 ) > g ( j 2 , j 3 ) g(j_1,j_2)>g(j_2,j_3) g(j1,j2)>g(j2,j3)的 j 2 j_2 j2
换言之,我们要转移的应该是满足 g ( j 1 , j 2 ) ≤ g ( j 2 , j 3 ) ≤ … g(j_1,j_2)≤g(j_2,j_3)≤ … g(j1,j2)≤g(j2,j3)≤…
用一个单调队列即可完成维护,每次在队首取值即可
队列中存的是决策点的下标,以队列中相邻两元素构成的 g ( ) g() g()为关键字单调递增
以 g ( j 1 , j 2 ) ≤ g ( j 2 , j 3 ) g(j_1,j_2)≤g(j_2,j_3) g(j1,j2)≤g(j2,j3)为例
①当 s [ i ] ≤ g ( j 1 , j 2 ) ≤ g ( j 2 , j 3 ) s[i]≤g(j_1,j_2)≤g(j_2,j_3) s[i]≤g(j1,j2)≤g(j2,j3)时, j 1 j_1 j1比 j 2 j_2 j2优, j 2 j_2 j2比 j 3 j_3 j3优 取 j 1 j_1 j1
②当 g ( j 1 , j 2 ) ≤ s [ i ] ≤ g ( j 2 , j 3 ) g(j_1,j_2)≤s[i]≤g(j_2,j_3) g(j1,j2)≤s[i]≤g(j2,j3)时, j 2 j_2 j2比 j 1 j_1 j1优, j 2 j_2 j2比 j 3 j_3 j3优 弹出 j 1 j_1 j1取 j 2 j_2 j2
③当 g ( j 1 , j 2 ) ≤ g ( j 2 , j 3 ) ≤ s [ i ] g(j_1,j_2)≤g(j_2,j_3)≤s[i] g(j1,j2)≤g(j2,j3)≤s[i]时, j 3 j_3 j3比 j 2 j_2 j2优, j 2 j_2 j2比 j 1 j_1 j1优 弹出 j 2 j_2 j2取 j 3 j_3 j3
然后再考虑用较为简便的“数形结合”
发现这恰好与上面的代数一一对应,将g看成一条直线,将s[i]也看成一条直线
分别讨论当 s [ i ] s[i] s[i]的斜率① x < j 1 j 2 x < j_1j_2 x<j1j2② j 1 j 2 < x < j 2 j 3 j_1j_2 < x < j_2j_3 j1j2<x<j2j3 ③ x > j 2 j 3 x > j_2j_3 x>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;
}