zoukankan      html  css  js  c++  java
  • bzoj1010: [HNOI2008]玩具装箱toy

    题面: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。但他希望费用最小.

    思路:O(n^2)的DP很容易:

    令前i个玩具长度之和为sum[i],则f[i]=min(f[j]+(sum[i]-sum[j]+i-j-1-L)^2)

    然后就是斜率优化了。

    f[i]=f[j]+(sum[i]-sum[j]+i-j-1-L)^2

    为了简化式子,令s[i]=sum[i]+i,m=s[i]-1-L;

    所以f[i]=f[j]+(m-s[j])^2

    f[i]=f[j]+m^2-2*m*s[j]+s[j]^2

    f[j]+s[j]^2=2*m*s[j]+f[i]-m^2

    这时我们就得到了一个形如y=kx+b的式子,其中k是与i有关的式子,x是关于j的式子,b是包含f[i]的式子。

    我们要使f[i]最小或最大,只要求截距b最小或最大时的j就可以取到答案

    这题中y=f[j]+s[j]^2 k=2*m x=s[j] b=f[i]-m^2

    要使f[i]最小,那么就要使截距b最小,我们把状态抽象成点,观察图象

    j和k两个点在斜率为2*m时哪个更优呢,当然是slope(j,k)<2*m时k更优,因为这题中查询的斜率递增,所以以后k也比j优


    又因为这题x是递增的,所以我们用队列维护一下,slope(q[head],q[head+1])<2*m时,队头元素就可以删去了,删到不能删为止

    加入一个新点i后我们就要考虑队列中那些点变得没有意义了,画图得知,对于队尾的点,slope(q[tail],q[tail-1])>slope(q[tail],i),那么队尾就没有意义了,此时我们就可以删去。


    所以每次转移时只要找队头元素即可。

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const int maxn=50010;
    int n,L,q[maxn];
    ll sum[maxn],s[maxn],f[maxn],m;char ch;
    double slope(int i,int j){return 1.0*(f[i]+s[i]*s[i]-f[j]-s[j]*s[j])/(s[i]-s[j]);}
    
    void read(ll &x){
    	for (ch=getchar();!isdigit(ch);ch=getchar());
    	for (x=0;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
    }
    
    int main(){
    	scanf("%d%d",&n,&L);
    	for (int i=1;i<=n;i++) read(sum[i]),sum[i]+=sum[i-1],s[i]=sum[i]+i;
    	int head=0,tail=0;f[0]=q[0]=0;
    	for (int i=1;i<=n;i++){
    		m=s[i]-L-1;
    		while (head<tail&&slope(q[head+1],q[head])<=2*m) head++;
    		int j=q[head];f[i]=f[j]+(m-s[j])*(m-s[j]);
    		while (head<tail&&slope(q[tail],q[tail-1])>=slope(i,q[tail])) tail--;
    		q[++tail]=i;
    	}
    	printf("%lld
    ",f[n]);
    	return 0;
    }


  • 相关阅读:
    【Anagrams】 cpp
    【Count and Say】cpp
    【Roman To Integer】cpp
    【Integer To Roman】cpp
    【Valid Number】cpp
    重构之 实体与引用 逻辑实体 逻辑存在的形式 可引用逻辑实体 不可引用逻辑实体 散弹式修改
    Maven项目聚合 jar包锁定 依赖传递 私服
    Oracle学习2 视图 索引 sql编程 游标 存储过程 存储函数 触发器
    mysql案例~tcpdump的使用
    tidb架构~本地化安装
  • 原文地址:https://www.cnblogs.com/thythy/p/5493586.html
Copyright © 2011-2022 走看看