当前位置: 代码迷 >> 综合 >> [bzoj2726][DP]任务安排
  详细解决方案

[bzoj2726][DP]任务安排

热度:78   发布时间:2023-12-19 05:17:02.0

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;
}