BZOJ_2726_[SDOI2012]任务安排_斜率优化+二分
BZOJ_2726_[SDOI2012]任务安排_斜率优化+二分
Description
机器上有N个需要处理的任务,它们构成了一个序列。这些任务被标号为1到N,因此序列的排列为1,2,3...N。这N个任务被分成若干批,每批包含相邻的若干任务。从时刻0开始,这些任务被分批加工,第i个任务单独完成所需的时间是Ti。在每批任务开始前,机器需要启动时间S,而完成这批任务所需的时间是各个任务需要时间的总和。注意,同一批任务将在同一时刻完成。每个任务的费用是它的完成时刻乘以一个费用系数Fi。请确定一个分组方案,使得总费用最小。
Input
第一行两个整数,N,S。
接下来N行每行两个整数,Ti,Fi。
Output
一个整数,为所求的答案。
Sample Input
5 1
1 3
3 2
4 3
2 3
1 4
1 3
3 2
4 3
2 3
1 4
Sample Output
153
设F[i]表示前i个任务完成后最小的代价。
然后把后面的代价提前计算。
设sx[],sy[]分别为x,y的前缀和。
F[i]=F[j]+(m+sx[i]-sx[j])*(sy[n]-sy[j])。设sum=sy[n]。
然后斜率优化:F[i]=-sy[j]*sx[i] -sx[j]*sum-m*sy[j]+sx[j]*sy[j] +m*sum+sx[i]*sum
把决策点当成直线,斜率-sy[j]单调减。
于是维护一个上凸壳。
但是每次查询的X(i)=sx[i]不一定单调。
因此不能乱弹队首。每次二分一下当前凸壳上交点离X(i)最近的大于X(i)位置,这个位置最优。
代码:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; #define N 300050 int n,m,xx[N],yy[N],Q[N],l,r; ll f[N],sx[N],sy[N],sum; ll K(int j) { return -sy[j]; } ll B(int j) { return f[j]-sx[j]*sum-m*sy[j]+sx[j]*sy[j]; } ll Y(int i,int j) { return K(j)*sx[i]+B(j); } bool cover(int p1,int p2,int p3) { // return (K(p1)-K(p3))*(B(p2)-B(p1))>=(K(p1)-K(p2))*(B(p3)-B(p1)); return (K(p2)-K(p3))*(B(p1)-B(p3))<=(B(p3)-B(p2))*(K(p3)-K(p1)); } bool check(int p1,int p2,int i) { return B(p1)-B(p2)>=sx[i]*(K(p2)-K(p1)); } int main() { scanf("%d%d",&n,&m); int i,j; for(i=1;i<=n;i++) { scanf("%d%d",&xx[i],&yy[i]); sx[i]=sx[i-1]+xx[i]; sy[i]=sy[i-1]+yy[i]; } sum=sy[n]; memset(f,0x3f,sizeof(f)); f[0]=0; /*for(i=1;i<=n;i++) { for(j=0;j<i;j++) { f[i]=min(f[i],f[j]+(m+sx[i]-sx[j])*(sum-sy[j])); } }*/ r=1; /*for(i=1;i<=n;i++) { while(l<r-1&&Y(i,Q[l])>=Y(i,Q[l+1])) l++; j=Q[l]; printf("%d\n",j); f[i]=Y(i,j)+m*sum+sx[i]*sum; while(l<r-1&&cover(Q[r-2],Q[r-1],i)) r--; Q[r++]=i; }*/ for(i=1;i<=n;i++) { int ll=l,rr=r-1; while(ll<rr) { int mid=(ll+rr)>>1; if(check(Q[mid],Q[mid+1],i)) ll=mid+1; else rr=mid; } j=Q[ll]; //printf("%d\n",j); f[i]=Y(i,j)+m*sum+sx[i]*sum; while(l<r-1&&cover(Q[r-2],Q[r-1],i)) r--; Q[r++]=i; } printf("%lld\n",f[n]); }