1 玩具装箱[HNOI2008]
solution
回到本题,设前缀和为(sum[i]),由题意易得dp方程:
但这个方程是(O(n^2))的,显然不满足要求,因此需要进行优化
(以下称两点斜率为(slope(A,B)))
令(a[i]=sum[i]+i),(b[i]=sum[i]+i+L+1)(这一步是为了简化计算)
则
展开得
移项得
将(b[j])看作(x),(dp[j]+b[j]^2)看作(y),这个式子就可以看作一条斜率为(2 cdot a[i])的直线
而对于每个(i)来说,(a[i])都是确定的
接下来的步骤和线性规划很相似
(dp[i])的含义转化为:当上述直线过点(P(b[j],dp[j]+b[j]^2))时,直线在(y)轴的截距加上(a[i]^2)(一个定值)
而题目即为找这个截距的最小值
因此,类似线性规划,我们将这条直线从下往上平移,直到过一个符合要求的点时停下,此时截距即为最小
code
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn=5e5+10;
const int mod=1e4+7;
#define int long long
int read(){
int a=0,op=1;char c=getchar();
while(c<'0'||c>'9') {if(c=='-') op=-1;c=getchar();}
while(c>='0'&&c<='9') a*=10,a+=c^48,c=getchar();
return a*op;
}
int n,L;
double sum[maxn],dp[maxn];
int head,tail,q[maxn];
double a(int x){return sum[x]+x;}
double b(int x){return a(x)+L+1;}
double X(int x){return b(x);}
double Y(int x){return dp[x]+b(x)*b(x);}
double k(int A,int B){return (Y(A)-Y(B))/(X(A)-X(B));}
double d_read(){
double p;scanf("%lf",&p);return p;
}
#undef int
int main(){
#define int long long
n=read(),L=read();
for(int i=1;i<=n;i++) sum[i]=d_read(),sum[i]+=sum[i-1];
head=tail=1;
for(int i=1;i<=n;i++){
while(head<tail&&k(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&&k(i,q[tail-1])<k(q[tail-1],q[tail])) --tail;
q[++tail]=i;
}
printf("%lld
",(int)dp[n]);
return 0;
}
2 任务安排
solution
方程:(f[i])代表前(i)个任务分成若干批产生的最小费用
转移:(f[i]=min_{i=0}^{i-1}(f[j]+t[i]*(c[i]-c[j])+S*(c[n]-c[j])))其中,t,ct,ct,c分别是任务时间和费用的前缀和的数组
考虑将状态转移方程化简: (f[j]=(S+t[i])*c[j]+f[i]-t[i]*c[i]-S*c[n])
注意:我们这里把(min)去掉了,是把(j)的取值集合所映射的(f[j])和(c[j])分别作为函数的(f(x))和(x)
那么这个一次函数的斜率(k)就等于((S+t[i])),而截距(b)等于(f[i]-t[i]*c[i]-S*c[n])
我们想让这个(f[i])最小,那么其实就等价于(b)最小,在坐标系中,如果我们拿一个已知斜率的直线向上滑动,当它第一次碰见取值集合内的点((c[j],f[j]))时,就取到了它的最小值
code
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn=5e5+10;
const int mod=1e4+7;
#define int long long
int read(){
int a=0,op=1;char c=getchar();
while(c<'0'||c>'9') {if(c=='-') op=-1;c=getchar();}
while(c>='0'&&c<='9') a*=10,a+=c^48,c=getchar();
return a*op;
}
int n,L;
double sum[maxn],dp[maxn];
double t[maxn],c[maxn];
int head,tail,s,q[maxn];
double d_read(){
double p;scanf("%lf",&p);return p;
}
double k(int a,int b){return (dp[a]-dp[b])/(c[a]-c[b]);}
double b(int x){return t[x]*c[x]+s*c[n];}
double a(int x){return s+t[x];}
#undef int
int main(){
#define int long long
n=read(),s=read();
for(int i=1;i<=n;i++) t[i]=d_read(),c[i]=d_read();
for(int i=1;i<=n;i++) t[i]+=t[i-1],c[i]+=c[i-1];
dp[0]=0;
head=tail=1;
for(int i=1;i<=n;i++){
while(head<tail&&k(q[head],q[head+1])<=a(i)) ++head;
dp[i]=dp[q[head]]+b(i)-c[q[head]]*a(i);
while(head<tail&&k(i,q[tail])<k(q[tail-1],q[tail])) --tail;
q[++tail]=i;
}
printf("%lld
",(int)dp[n]);
return 0;
}