Description
机器上有N个需要处理的任务,它们构成了一个序列。这些任务被标号为1到N,因此序列的排列为1,2,3…N。这N个任务被分成若干批,每批包含相邻的若干任务。从时刻0开始,这些任务被分批加工,第i个任务单独完成所需的时间是Ti。在每批任务开始前,机器需要启动时间S,而完成这批任务所需的时间是各个任务需要时间的总和。注意,同一批任务将在同一时刻完成。每个任务的费用是它的完成时刻乘以一个费用系数Ci。请确定一个分组方案,使得总费用最小。
Input
第一行两个整数,N,S。 接下来N行每行两个整数,Ti,Ci。
Output
一个整数,为所求的答案。
Sample Input
5 1
1 3
3 2
4 3
2 3
1 4
Sample Output
153
题解
设t[i]表示t的前缀和 c[i]表示f的前缀和
首先dp方程给出
f[i]=min(f[j]+t[i]∗(c[i]−c[j])+S∗(c[n]−c[j])) f [ i ] = m i n ( f [ j ] + t [ i ] ∗ ( c [ i ] − c [ j ] ) + S ∗ ( c [ n ] − c [ j ] ) )
此处影响释放给了后面的任务
大力斜率优化,可以发现在 j<k j < k 且
f[j]−f[k]c[j]−c[k]<t[i]+S f [ j ] − f [ k ] c [ j ] − c [ k ] < t [ i ] + S
k比j优秀
于是维护一个下凸壳
观察到t[i]没有单调性,不能直接单调队列
我们二分出第一个左边线段斜率小于 S+t[i] S + t [ i ] ,右边线段斜率大于 S+t[i] S + t [ i ] 的点
这个点就是最佳决策点
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
LL f[310000],c[310000],t[310000],S;
int n;
double slop(int j,int k){return (double)(f[j]-f[k])/(c[j]-c[k]);}
int li[310000],head,tail;
int fdans(LL gg)
{
int l=head,r=tail;
while(l<r)
{
int mid=(l+r)/2;
if((f[li[mid]]-f[li[mid+1]])>gg*(c[li[mid]]-c[li[mid+1]]))l=mid+1;
// if(slop(li[mid],li[mid+1])<gg)l=mid+1;
else r=mid;
}
return li[l];
}
int main()
{
// freopen("a.in","r",stdin);
// freopen("a.out","w",stdout);
scanf("%d%lld",&n,&S);
for(int i=1;i<=n;i++)scanf("%lld%lld",&t[i],&c[i]),t[i]+=t[i-1],c[i]+=c[i-1];
f[0]=0;
li[1]=0;head=tail=1;
for(int i=1;i<=n;i++)
{
int p=fdans(t[i]+S);
f[i]=f[p]+t[i]*(c[i]-c[p])+S*(c[n]-c[p]);
while(head<tail && (f[li[tail-1]]-f[li[tail]])*(c[i]-c[li[tail]])<=(f[i]-f[li[tail]])*(c[li[tail-1]]-c[li[tail]]))tail--;
li[++tail]=i;
}
printf("%lld\n",f[n]);
return 0;
}