[HNOI2008]玩具装箱

「HNOI2008」玩具装箱

题目大意

n 个玩具,第 i 个玩具的价值为 ci 。这 n 个玩具排成一排,要求将这些玩具分成若干段,对于一段 [l,r] ,其代价为 (rl+Σi=lrciL)2 。其中 L 是一个常量,求分段最小代价。(1n5×104,1L,ci107)

题解(斜率优化)

比较容易想到的是直接dp做:

fi 为前i个物品分成若干段的最小代价。

那么状态转移方程为:

fi=minj<i{fj+((ij1)+preiprejL)2}=minj<i{fj+(prei+iprejj1L)2}

那么这样的做法时间复杂度是 (n2) 的,是过不了的。

考虑优化:

对于上述状态转移方程,我们在进行变换:

为方便理解,令 ai 等于 prei+iL=L+1 。原式子等于fi=minj<i{fj+(aiajL)2}

将与 j 有关的放一边,我们能得到:

fi(aiL)2=minj<i{fj+aj22aj(aiL)}

我们要找到一个 j 使得 fi 最小,我们便设与 j 相关的数设为变量。通过上面的式子我们可以发现,若我们将一次函数的斜截式 y=kx+b 代入其中,也就是 b=ykx ,将 i 相关的令作常量,与 j 相关的令作变量,即:

xj=ajyj=fj+aj2ki=2(aiL)bi=fi(aiL)2

上述转移方程可以写成 bi=minj<i{yjkixj} ,首先 ki 是一个常量,我们把 {xj,yj} 看成平面直角坐标系上的点,这样我们就把原问题转化成了选择一个合适的点 j (1j<i) ,使得截距 bi 最小。

斜率优化

如上图所示,我们将图中的直线向上平移,第一个接触到的点B显然是使得截距最小的点,且我们会发现能使得截距最小的点一定是下凸点,以点B为例,前一个点A,和后一个点B构成的斜率满足 kAB<kBC ,所以点B是下凸点,同时点B是合适点的另一条件是 kABki<kBC ,且本题中,ki 是递增的,所以,我们可以用一个单调队列维护下凸点(即连续点的斜率递增的点),步骤如下:

  1. 首先是在队首 head ,判断是否 khead,head+1>ki ,如果不是说明点 head 并不是合适点,删除即可,直到满足 khead,head+1>ki
  2. 之后根据合适点更新 fi
  3. 加入新的点进入队尾 tail 前,先判断 ktail1,tail<ktail,new ,如果不成立,则队尾的点不是下凸点,删除并且找到满足的即可

步骤1

对于步骤1,由于 ki 的增大,我们发现点A,B都不满足合适点的要求了,直接贪心的删去就好了。

步骤3

对于步骤3,新加入点E后,点D并不构成下凸点的条件,无论 ki 变成什么,点D都不可能是合适点,那么直接在队尾中删去

具体实现看下列代码

#include <algorithm>
#include <cstdio>
using ll = long long;

const int N = 5e4 + 5;

ll dp[N], pre[N], q[N], n, L, head, tail;

ll a(int i){
    return pre[i] + i;
}

ll Y(int i){
    return dp[i] + a(i) * a(i);
}

double K(int i, int j){
    return 1. * (Y(i) - Y(j)) / (a(i) - a(j));
}


int main(void){
    scanf("%lld%lld", &n, &L);
    L++;
    for(int i = 1; i <= n; ++i){
        int a;
        scanf("%d", &a);
        pre[i] = pre[i - 1] + a;
    }
    head = tail = 1;
    for(int i = 1; i <= n; ++i){
        while(head < tail && K(q[head], q[head + 1]) < 2 * (a(i) - L))head++;
        dp[i] = dp[q[head]] + (a(i) - a(q[head]) - L) * (a(i) - a(q[head]) - L);
        while(head < tail && K(q[tail], q[tail - 1]) > K(i, q[tail]))tail--;
        q[++tail] = i;
    }
    printf("%lld", dp[n]);
    return 0;
}
posted @   言葉の庭  阅读(52)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示