首页 > 其他分享 >P3195 [HNOI2008]玩具装箱

P3195 [HNOI2008]玩具装箱

时间:2022-08-31 12:48:16浏览次数:42  
标签:P3195 50005 int sum times HNOI2008 装箱 dp define

给定序列 \(C\) ,将原序列拆成几个部分,每个部分 \([i,j]\) 费用为 \(j-i+\sum^{j}_{k=i}C_k\) ,最小化费用。 \(n\leq 5\times 10^4\) 。


定义 \(sum[i]\) 为前 \(i\) 项的前缀和, \(dp[i]\) 为将前 \(i\) 项拆成若干个区间的最小费用。那么有一个朴素的 \(O(n^2)\) 做法,直接枚举 \(j\) , \(dp[i]=min\{dp[j]+(sum[i]+i-sum[j]-j-L)^2\}\) , \(dp[n]\) 就是答案。

考虑优化式子,令 \(P[i]=sum[i]+i\) , \(Q[i]=sum[i]+i+L+1\) ,那么

\[dp[i]=dp[j]+(P[i]-Q[j])^2 \]

\[dp[i]=dp[j]+P[i]^2-2\times P[i]\times Q[j]+Q[j]^2 \]

\[2\times P[i]\times Q[j]+dp[i]-P[i]^2=dp[j]+Q[j]^2 \]

令 \(x=Q[j],y=dp[j]+Q[j]^2\) ,原式变成 \(y=2P[i]x-P[i]^2+dp[i]\) 。

那么 \(b\) 的意义就是 \(P[i]^2+dp[i]\) ,又 \(P[i]=sum[i]+i\) 是定值,所以 \(b\) 尽可能的小,就会使 \(dp[i]\) 小。同时注意到 \(2P[i]\) 一定是单调递增的,那么该直线的斜率一定单增。将每一个 \(C_i\) 都转化成点 \((Q[j],dp[j]+Q[j]^2)\) 。如下图,在下凸包上方的点一定不会是最优的,那么可以提前删除。同时,这与单调队列相似,在之后的点中斜率大的有优势,所以可以记录相邻两点构成直线的斜率,然后像单调队列一样 poppush 即可。

#include<bits/stdc++.h>
using namespace std;

int n,L;
double c[50005];


double sum[50005],dp[50005];
int head,tail,q[50005];

#define a(i) (sum[i]+i)
#define b(i) (a(i)+L+1)
#define X(i) b(i)
#define Y(i) (dp[i]+(b(i)*b(i)))
#define slope(i,j) (Y(i)-Y(j))/(X(i)-X(j))

int main(){
	scanf("%d %d",&n,&L);
	for(int i=1;i<=n;++i){
		scanf("%lf",&c[i]);
		sum[i]=c[i]+sum[i-1];
	}
	head=tail=1;
	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;
	}
	printf("%lld\n",(long long)dp[n]);
	return 0;
}

标签:P3195,50005,int,sum,times,HNOI2008,装箱,dp,define
From: https://www.cnblogs.com/zhouzizhe/p/16642681.html

相关文章

  • 装箱与拆箱
    Object类在C#语言中,Object类是所有类的父类,在C#中所有的类(内置的,我们自己创建的)都直接或者间接继承自Object类。Object是类,object是类型。(类与系统关键字的语法颜色区别)......
  • 1090 危险品装箱——25分
    集装箱运输货物时,我们必须特别小心,不能把不相容的货物装在一只箱子里。比如氧化剂绝对不能跟易燃液体同箱,否则很容易造成爆炸。本题给定一张不相容物品的清单,需要你检查每......