Live2d Test Env

BZOJ2726:任务安排(DP+斜率优化+二分)

机器上有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

Sample Output

153

思路:注意是只有一台机器,所以时间是累加的,那么影响到[j,N]。所以列出方程: f[i]=min():  f[j]+(sum2[N]-sum2[j])*(sum1[i]-sum1[j]+M) 

           直接dp复杂度是O(N^2),使用效率优化:

//b=y-kx+c; --> f[i]=(-sum1[i]*sum2[j])+(f[j]+sum1[j]*sum2[j]-sum*sum1[j]-M*sum2[j])+(M*sum+sum*sum1[j]);
其中k之和i有关,y之和j有关,b就是f[i],c是常数:k=sum1[i],y=f[j]-sum2[N]*sum1[j]+sum2[j]*sum1[j]-sum2[j]*M;

          可以看到我们需要维护一个斜率上升的凸包,由于K=sum1[i]没有说递增,所以我们不能弹出栈顶,求的时候用二分求得凸包极值。

注意:1,二分的时候,二分区间[0,top],0代表的是,从头到尾都选,不能忽略。

           2,每个新的i都要插入,插入当前i时,要维护斜率递增。    

           3,维护的图像的x和y分别的 y=kx+b的x和y,所以维护斜率,弹出栈尾比较斜率时,x是sum2[q[top]],而不是q[top];

//b=y-kx+c; --> f[i]=(-sum1[i]*sum2[j])+(f[j]+sum1[j]*sum2[j]-sum*sum1[j]-M*sum2[j])+(M*sum+sum*sum1[j]);
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1000010;
ll sum1[maxn],sum2[maxn],f[maxn],M;
int q[maxn],top,N;
ll Y(int j){ return f[j]-sum2[N]*sum1[j]+sum2[j]*sum1[j]-sum2[j]*M; }
ll getans(int i,int j){return f[j]+(sum2[N]-sum2[j])*(sum1[i]-sum1[j]+M);; }
int main()
{
    int i; scanf("%d%lld",&N,&M);
    for(i=1;i<=N;i++){
        scanf("%lld%lld",&sum1[i],&sum2[i]);
        sum1[i]+=sum1[i-1]; sum2[i]+=sum2[i-1];
    }
    for(int i=1;i<=N;i++){
        int L=0,R=top,ans=top;
        while(L<=R){
            int Mid=(L+R)>>1;
            if(getans(i,q[Mid])<=getans(i,q[Mid+1])) R=Mid-1,ans=Mid; else L=Mid+1;
        }
        f[i]=getans(i,q[ans]);
        while (top>0&& 1ll*(Y(q[top])-Y(q[top-1]))*(sum2[i]-sum2[q[top]])>=(Y(i)-Y(q[top]))*(sum2[q[top]]-sum2[q[top-1]]))
top--; q[++top]=i; //while语句里不是dx的时候不是下边之间减,是方程组的sum2来减。 } printf("%lld\n",f[N]); return 0; }

 

posted @ 2018-06-29 17:57  nimphy  阅读(510)  评论(0编辑  收藏  举报