题目

机器上有N个需要处理的任务,它们构成了一个序列。这些任务被标号为1到N,因此序列的排列为1,2,3...N。这N个任务被分成若干批,每批包含相邻的若干任务。从时刻0开始,这些任务被分批加工,第i个任务单独完成所需的时间是Ti。在每批任务开始前,机器需要启动时间S,而完成这批任务所需的时间是各个任务需要时间的总和。注意,同一批任务将在同一时刻完成。每个任务的费用是它的完成时刻乘以一个费用系数Fi。请确定一个分组方案,使得总费用最小。

思路

首先显然是\(dp\)划分状态。
\[ dp[i]=min(dp[j]+t[i]*(f[i]-f[j])+s*f[n]-s*f[j]) \]

不难看出可以斜率优化。

然而不知道出题人怎么想的,时间竟然可以出现负数,这样对于\(x\)不单调的斜率优化,就只能用CDQ分治维护凸包。

具体而言,就是和惯常的CDQ分治一样,分别将\([l,mid]\),\([mid+1,r]\)区间排序,\([l,mid]\)建一个凸包,回答\([mid+1,r]\)的询问。

一个while打成if调了一个晚上

代码

#include<bits/stdc++.h>
#define M 300005
#define LL long long
using namespace std;
LL t[M],f[M],dp[M],s;
int n,qcnt,tcnt,top;
struct node{
    LL x,y;
    bool operator < (const node& res)const{
        if(x!=res.x)return x<res.x;
        return y>res.y;
    }
}Q[M],stk[M],to[M];
bool cmp(node a,node b,node c){
    return (long double)(c.y-a.y)*(a.x-b.x)<=(long double)(a.y-b.y)*(c.x-a.x);
}
LL calc(int cur,int i){
    return  (long double)stk[cur].y-(long double)t[to[i].y]*stk[cur].x+(long double)t[to[i].y]*f[to[i].y]+(long double)s*f[n];
}
void CDQ(int l,int r){
    if(l==r)return;
    int mid=(l+r)>>1;
    CDQ(l,mid);qcnt=0;
    for(int i=l;i<=mid;i++)
        Q[++qcnt]=(node){f[i],dp[i]-s*f[i]};
    sort(Q+1,Q+qcnt+1);top=0;
    for(int i=1;i<=qcnt;i++){
        while(top>1&&cmp(stk[top],stk[top-1],Q[i]))top--;
        stk[++top]=Q[i];
    }
    tcnt=0;
    for(int i=mid+1;i<=r;i++)
        to[++tcnt]=(node){t[i],i};
    sort(to+1,to+tcnt+1);
    int cur=1;
    for(int i=1;i<=tcnt;i++){
        while(cur<top&&calc(cur+1,i)<calc(cur,i))cur++;
        dp[to[i].y]=min(dp[to[i].y],calc(cur,i));
    }
    CDQ(mid+1,r);
}
int main(){
    memset(dp,60,sizeof(dp));dp[0]=0;
    scanf("%d%lld",&n,&s);
    for(int i=1;i<=n;i++){
        scanf("%lld%lld",&t[i],&f[i]);
        t[i]+=t[i-1];f[i]+=f[i-1];
    }
    CDQ(0,n);
    printf("%lld\n",dp[n]);
    return 0;
}