题意&思路:
题意:把大的区间分段,每一段的有一个花费,求总的最小花费。
这种分段的花费的题,貌似成了斜率dp的一眼题了,上次CF的E题比赛的时候,实力套路一波斜率dp的推导过程,感觉还不错,还学习了下二维的斜率优化。
首先,你假设就按二维来做,然后看是否满足能够去斜率优化,写出二维的转移就是:
Dp[i][j] : 区间 [1,i] 分为 j 段的最小花费
Dp[i][j]=min(dp[k][j−1]+cost(k+1,i)), Cost(k+1,i)=M+(sigmaa[k+1….i])2 然后呢,假设 k1<k2<i ,而且 K2 比 K1 更优,则:
代入原来的式子,可以推出一个式子,不等式的一边只含有i,另外一边也不含有i:
(dp[k2]+num[k2]2−(dp[k1]+num[k1]2))/(2∗(num[k2]−num[k1]))<sum[i]满足斜率优化的条件,也就是只要满足上面的式子,就表示 K2 比 K1 更优,那么 K1 就不会更新到后面的值。
所以就用队列来维护一个凸包,进队和出队操作就是:
while(l < r && slope(Q[l+1], Q[l]) <= sum[i]) l ++; // 舍去点
while(l < r && slope(i, Q[r]) <= slope(Q[r], Q[r-1])) r --; // 找到第一个满足构成凸包的点
Q[++r] = i;
代码:
#include <bits/stdc++.h>
#define PB push_back
#define FT first
#define SD second
#define MP make_pair
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> P;
const int N = 5e5 + 10,MOD = 7+1e9;
int n, m;
int a[N], sum[N], Q[N];
LL dp[N];
int l, r;
double slope(int i,int j)
{
if (sum[i] == sum[j])
return 1e233;
double up = dp[i] - dp[j] + 1LL*sum[i]*sum[i] - 1LL*sum[j]*sum[j];
double down = 2 * (sum[i] - sum[j]);
return up / down;
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1;i <= n;++ i) scanf("%d", &a[i]), sum[i] = a[i] + sum[i-1];
l = r = 1;
Q[r] = 0;
for(int i = 1;i <= n;i ++)
{
while(l < r && slope(Q[l+1], Q[l]) <= sum[i]) l ++;
int now = Q[l];
dp[i] = dp[now] + m + 1LL*(sum[i]-sum[now])*(sum[i]-sum[now]);
while(l < r && slope(i, Q[r]) <= slope(Q[r], Q[r-1])) r --;
Q[++r] = i;
}
printf("%lld\n", dp[n]);
return 0;
}