[BZOJ] 1010 [HNOI2008]玩具装箱toy

Time Limit: 1 Sec  Memory Limit: 162 MB
Submit: 12664  Solved: 5539
[Submit][Status][Discuss]
Description
  P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京。他使用自己的压缩器进行压
缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中。P教授有编号为1...N的N件玩具,第i件玩具经过
压缩后变成一维长度为Ci.为了方便整理,P教授要求在一个一维容器中的玩具编号是连续的。同时如果一个一维容
器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物,形式地说如果将第i件玩具到第j个玩具放到一
个容器中,那么容器的长度将为 x=j-i+Sigma(Ck) i<=K<=j 制作容器的费用与容器的长度有关,根据教授研究,
如果容器长度为x,其制作费用为(X-L)^2.其中L是一个常量。P教授不关心容器的数目,他可以制作出任意长度的容
器,甚至超过L。但他希望费用最小.

Input
  第一行输入两个整数NL.接下来N行输入Ci.1<=N<=50000,1<=L,Ci<=10^7

Output
  输出最小费用

Sample Input
5 4

3

4

2

1

4
Sample Output
1
HINT
Source

[Submit][Status][Discuss]

斜率优化第一题,感觉很玄妙。
dp(i)=min(dp(i)+(sum(i)-sum(j)+i-j-1-L)^2)
设a(i)=sum(i)+i,b(i)=sum(i)+i+1+L
可见a,b均单增
接着原式可以化为
dp(i)= dp(j)+(a(i)-b(j))^2
2a(i)b(j)+dp(i)-a(i)^2=dp(j)+b(j)^2
设X=b(j),Y=dp(j)+b(j)^2
Y=2a(i)X+dp(i)-a(i)^2
这是一条斜率为2a(i),截距为dp(i)-a(i)^2的直线
对于一个i,a(i)是定值,要最小化dp(i),就要最小化截距

这个直线过点(b(j),dp(j)+b(j)^2),也就是刚才代换的X和Y
b(j)单增,所以这些点横坐标单增,可以用单调队列维护一个凸包

一个点P是最优解当且仅当slope(P,P+1)>=2a(i),这时候就可以与直线相切了。

维护的时候,在队首删掉斜率小于2a(i)的点,因为随着a(i)单增,这些点一定不可能成为最优了
在队尾删掉斜率大于slope(Pi,Ptail)的点,否则就够不成凸包了。

有几个不理解的地方,为什么公式里的min可以去掉?这样推导的动力是什么?

#include<iostream>
#include<cstdio>
#define int long long
using namespace std;

const int MAXN=500005;

inline int rd(){
    int ret=0,f=1;char c;
    while(c=getchar(),!isdigit(c))f=c=='-'?-1:1;
    while(isdigit(c))ret=ret*10+c-'0',c=getchar();
    return ret*f;
}

int n,L;
int dp[MAXN],sum[MAXN];

inline double a(int x){return sum[x]+x;}
inline double b(int x){return sum[x]+x+1+L;}
inline double X(int x){return b(x);}
inline double Y(int x){return dp[x]+b(x)*b(x);}
inline double slope(int x,int y){return 1.0*(Y(y)-Y(x))/(X(y)-X(x));}

int Q[MAXN],head=1,tail=1;

signed main(){
    n=rd();L=rd();
    for(int i=1;i<=n;i++){
        int x=rd();
        sum[i]=sum[i-1]+x;
    }
    for(int i=1;i<=n;i++){
        while(head<tail&&slope(Q[head],Q[head+1])<2*a(i)) head++;
        dp[i]=dp[Q[head]]+(a(i)-b(Q[head]))*(a(i)-b(Q[head]));
        while(head<tail&&slope(i,Q[tail-1])<slope(Q[tail-1],Q[tail])) tail--;
        Q[++tail]=i;
    }
    cout<<dp[n]<<endl;
    return 0;
}

本文来自博客园,作者:GhostCai,转载请注明原文链接:https://www.cnblogs.com/ghostcai/p/9247405.html

原文地址:https://www.cnblogs.com/ghostcai/p/9247405.html