BZOJ2726 [SDOI2012]任务安排 【斜率优化 + cdq分治】
题目
机器上有N个需要处理的任务,它们构成了一个序列。这些任务被标号为1到N,因此序列的排列为1,2,3...N。这N个任务被分成若干批,每批包含相邻的若干任务。从时刻0开始,这些任务被分批加工,第i个任务单独完成所需的时间是Ti。在每批任务开始前,机器需要启动时间S,而完成这批任务所需的时间是各个任务需要时间的总和。注意,同一批任务将在同一时刻完成。每个任务的费用是它的完成时刻乘以一个费用系数Fi。请确定一个分组方案,使得总费用最小。
输入格式
第一行两个整数,N,S。
接下来N行每行两个整数,Ti,Fi。
输出格式
一个整数,为所求的答案。
输入样例
5 1
1 3
3 2
4 3
2 3
1 4
输出样例
153
提示
[1, 4] 0<N<=1000 0<=S<=2^8 0<=Ti<=2^8 0<=Fi<=2^8
[5, 12] 0<N<=300000 0<=S<=2^8 0<=Ti<=2^8 0<=Fi<=2^8
[13, 20] 0<N<=100000 0<=S<=2^8 -(28)<=Ti<=28 0<=Fi<=2^8
题解
时间可以为负是什么鬼。。。
由于一个任务代价是完成时间乘以权值,所以我们可以看做没过一个时间都要花费当前所有没完成的任务的权值
设\(f[i]\)为前\(i\)个任务决策完成的最小代价,我们很容易可以得到一个dp方程
\(f[i] = max(f[j] + (F[i] - F[j]) * (T[i] - T[j] + s))\)
其中F和T都指前缀和
稍微整理一下就是一个斜率优化的式子
但是由于\(Ti\)可以为负,所以T不保证单调,不能使用单调队列
要么用splay维护凸包,要么就用cdq分治
当然是选择简短好写的cdq分治啦
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LL long long int
using namespace std;
const int maxn = 300005,maxm = 100005;
const LL INF = 1000000000000000000ll;
inline int read(){
int out = 0,flag = 1; char c = getchar();
while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
return out * flag;
}
struct node{
LL t,x,y,k;
}e[maxn],t[maxn];
inline bool operator <(const node& a,const node& b){
return a.x == b.x ? a.y < b.y : a.x < b.x;
}
inline bool cmp(const node& a,const node& b){
return a.k < b.k;
}
int n;
LL T[maxn],w[maxn],f[maxn],s;
int st[maxn],top;
void cdq(int l,int r){
if (l == r){
e[l].x = w[l];
e[l].y = f[l] + w[l] * T[l] - s * w[l] - w[n] * T[l];
return;
}
int mid = l + r >> 1,li = l,ri = mid + 1;
for (int i = l; i <= r; i++){
if (e[i].t <= mid) t[li++] = e[i];
else t[ri++] = e[i];
}
for (int i = l; i <= r; i++) e[i] = t[i];
cdq(l,mid);
top = 0;
for (int i = l; i <= mid; i++){
while (top > 1 && (e[i].x - e[st[top - 1]].x) * (e[st[top]].y - e[st[top - 1]].y) - (e[i].y - e[st[top - 1]].y) * (e[st[top]].x - e[st[top - 1]].x) >= 0)
top--;
st[++top] = i;
}
int j = 1;
for (int i = mid + 1; i <= r; i++){
while (j < top && e[st[j + 1]].y - e[st[j]].y <= (e[st[j + 1]].x - e[st[j]].x) * e[i].k)
j++;
f[e[i].t] = min(f[e[i].t],-e[st[j]].x * e[i].k + e[st[j]].y + w[n] * e[i].k + w[n] * s);
}
cdq(mid + 1,r);
li = l; ri = mid + 1;
for (int i = l; i <= r; i++){
if (ri > r || (li <= mid && e[li] < e[ri])) t[i] = e[li++];
else t[i] = e[ri++];
}
for (int i = l; i <= r; i++) e[i] = t[i];
}
int main(){
n = read(); s = read();
for (int i = 1; i <= n; i++){
T[i] = T[i - 1] + read();
w[i] = w[i - 1] + read();
}
for (int i = 1; i <= n; i++){
e[i].t = i;
e[i].k = T[i];
f[i] = INF;
}
sort(e + 1,e + 1 + n,cmp);
cdq(0,n);
printf("%lld\n",f[n]);
return 0;
}