BZOJ 1010 [HNOI2008]玩具装箱toy 斜率优化dp

[HNOI2008]玩具装箱toy

斜率优化dp:

好久没有写斜率优化dp都忘记了这个东西到底是怎么回事。

对于斜率优化dp来说, 我们可以将一个 转移方程转换成 y = k x + b.

其中要求的东西在b上。

注意: 现在是给定了一堆点(x,y), 让你在固定k的前提下求出最小/最大的b是多少。

如果现在是维护最小的b。

那么我们需要维护出一个下凸壳。

如果k是递增的话, 我们可以不断的将队头不是最优的(x,y)去掉,留下最优的点作为对头。(讲道理,如果在求最小b的情况下, 这个k应该不会递减,不然就显得特别蠢,23333)。

如果k是随意的话,我们可以把下凸壳维护出来,然后二分找到最优解。

 

题解:

一开始列的方程应该为 dp[i] = min(dp[j] + (i-j-1 + sum[i] - sum[j] - L)^2)。

因为n=1e5,所以这个题目不能用n^2去转移。

可以令A = sum[i] + i, B = sum[j]+j+L+1

化简前面的式子,可得:dp[j] + B^2 = 2 * A * B + dp[i] - A^2.

y = dp[j] + B ^ 2, k = 2 * A, x = B, dp[i] - A ^ 2。

也就是上面说的y = k * x + b了。

通过维护一个下凸壳,我们可以在o(n)的复杂度内求出答案。

代码:

#include<bits/stdc++.h>
using namespace std;
#define Fopen freopen("_in.txt","r",stdin); freopen("_out.txt","w",stdout);
#define LL long long
#define ULL unsigned LL
#define fi first
#define se second
#define pb push_back
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lch(x) tr[x].son[0]
#define rch(x) tr[x].son[1]
#define max3(a,b,c) max(a,max(b,c))
#define min3(a,b,c) min(a,min(b,c))
typedef pair<int,int> pll;
const int inf = 0x3f3f3f3f;
const int _inf = 0xc0c0c0c0;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL _INF = 0xc0c0c0c0c0c0c0c0;
const LL mod =  (int)1e9+7;
const int N = 2e5 + 100;
int n, L;
int a[N];
int sta[N];
LL sum[N];
LL dp[N];
LL calb(int id, int i){
    LL B = sum[id] + id + L + 1;
    LL A = sum[i] + i;
    return dp[id] + B * B - 2 * A * B;
}
double slope(int x, int y){
    double x1 = sum[x] + x + L + 1;
    double y1 = dp[x] + x1 * x1;
    double x2 = sum[y] + y + L + 1;
    double y2 = dp[y] + x2 * x2;
    return (y1-y2) / (x1-x2);
}
int Ac(){
    scanf("%d%d", &n, &L);
    for(int i = 1; i <= n; ++i){
        scanf("%d", &a[i]);
        sum[i] = sum[i - 1] + a[i];
    }
    int L = 1, R = 1; sta[0] = 0;
    for(int i = 1; i <= n; ++i){
        while(L < R && calb(sta[L], i) > calb(sta[L+1], i)) ++L;
        LL A = sum[i] + i;
        dp[i] = calb(sta[L], i) + A * A;
        while(L < R && slope(i,sta[R]) < slope(sta[R], sta[R-1])) --R;
        sta[++R] = i;
    }
    printf("%lld\n", dp[n]);
    return 0;
}

int main(){
    Ac();
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/MingSD/p/10855934.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值