有 (N) 个任务排成一个序列在一台机器上等待执行,它们的顺序不得改变。机器会把这 (N) 个任务分成若干批,每一批包含连续的若干个任务。从时刻 (0) 开始,任务被分批加工,执行第 (i) 个任务所需的时间是 (t[i]) 。另外,在每批任务开始前,机器需要 (S) 的启动时间,故执行一批任务所需的时间是启动时间 (S) 加上每个任务所需时间之和。一个任务执行后,将在机器中稍作等待,直至该批任务全部执行完毕。也就是说,同一批任务将在同一时刻完成。每个任务的费用是它的完成时刻乘以一个费用系数 (c[i]) 。请为机器规划一个分组方案,使得总费用最小。
(1≤N≤3∗10^5)
(1≤S,Ti,Ci≤512)
设 (T[i]) 为时间的前缀和 , (C[i]) 是系数 (c) 的前缀和 . 在一批任务开始对后续任务产生影响时,就先把费用累加到答案中. 这就是"费用提前计算"的思想 .
对于每一次转移, 有 (chkmin(f[i], f[j] + (C[j] - C[i]) * T[i] + S * (C[n] - C[j])))
考虑到数据范围很像斜率优化 , 把式子转化为斜率优化的形式 :
(f[j] = (S + T[i]) * C[j] - S * C[n] - T[i] * C[i] + f[i])
发现斜率是固定的 , 当截距最小时 (f[i]) 最小 . 所以要维护 ((C[j],f[j])) 围成的下凸包 .
由于 (f[j]) 随 (C[j]) 单调递增 , ((S+T[i])) 也是单调递增的 , 所以可以用单调队列维护 : 只保留两点间斜率 (>=S+T[i]) 的点 , 入队时检查有没有斜率递增
时间复杂度 (O(n))
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define Debug(x) cout<<#x<<"="<<x<<endl
using namespace std;
typedef long long LL;
const int INF=1e9+7;
inline LL read(){
register LL x=0,f=1;register char c=getchar();
while(c<48||c>57){if(c=='-')f=-1;c=getchar();}
while(c>=48&&c<=57)x=(x<<3)+(x<<1)+(c&15),c=getchar();
return f*x;
}
const int N = 3e5 + 5;
int T[N], C[N], q[N], f[N];
int n, S;
int main(){
n = read(), S = read();
for(int i = 1; i <= n; ++i){
T[i] = T[i-1] + read();
C[i] = C[i-1] + read();
}
int l = 1, r = 1;
q[1] = 0; // 把0入队是因为j取0是合法的.
for(int i = 1; i <= n; ++i){
while(l < r && ((f[q[l+1]] - f[q[l]]) <= (S + T[i]) * (C[q[l+1]] - C[q[l]]))) l++;
f[i] = f[q[l]] - (T[i] + S) * C[q[l]] + C[i] * T[i] + S * C[n];
while(l < r && ((f[q[r]] - f[q[r-1]]) * (C[q[i]] - C[q[r]]) >= (f[q[i]] - f[q[r]]) * (C[q[r]] - C[q[r-1]]))) r--;
q[++r] = i;
}
printf("%d
", f[n]);
}
(1≤N≤3∗10^5 ; 0≤S,c[i]≤512 ; -512≤t[i]≤512)
完成的时间可以为负数 , 则 (S+T[i]) 不再具有单调性 . 所以在单调队列中二分查找出对应的位置 , 不需要出队 , 但需要维护入队时的斜率单调性
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define Debug(x) cout<<#x<<"="<<x<<endl
#define int long long
using namespace std;
typedef long long LL;
const int INF=1e9+7;
inline LL read(){
register LL x=0,f=1;register char c=getchar();
while(c<48||c>57){if(c=='-')f=-1;c=getchar();}
while(c>=48&&c<=57)x=(x<<3)+(x<<1)+(c&15),c=getchar();
return f*x;
}
const int N = 3e5 + 5;
int f[N], q[N], T[N], C[N];
int n, S;
inline int find(int L, int R, int x){
while(L < R){
int mid = (L + R) >> 1;
if((f[q[mid+1]] - f[q[mid]]) <= x * (C[q[mid+1]] - C[q[mid]])) L = mid + 1; ///
else R = mid;
}
return L;
}
signed main(){
n = read(), S = read();
for(int i = 1; i <= n; ++i){
T[i] = T[i-1] + read();
C[i] = C[i-1] + read();
}
int l = 1, r = 1;
for(int i = 1; i <= n; ++i){
int p = find(l, r, S + T[i]);
f[i] = f[q[p]] - (S + T[i]) * C[q[p]] + S * C[n] + T[i] * C[i];
while(l < r && ((f[q[r]] - f[q[r-1]]) * (C[i] - C[q[r]]) >= (f[i] - f[q[r]]) * (C[q[r]] - C[q[r-1]]))) r--;
q[++r] = i;
}
printf("%lld
", f[n]);
}