bzoj2726 [SDOI2012]任务安排

题目链接

problem

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

solution

f[i]表示前i个物品进行分组的最小花费。
下面的\(c_i\)表示题目描述中\(c_i\)的前缀和,\(t_i\)表示题目描述中\(t_i\)的前缀和。

考虑\(n^2\)做法

由于每次分组都会导致后面的时刻后移,所以花费系数就是\(c_n-c_j\)

\(f_i=min\{f_j+(c_n-c_j)(t_i-t_j+S)\},j\in[0,i)\)

\(n^2\)转移即可。

考虑优化成\(nlogn\)

将上面的式子展开并处理一下:

\(f_i=min\{f_j+c_nt_i-c_nt_j+c_nS-c_jt_i+c_jt_j-c_jS\}\\ =min\{t_i(c_n-c_j)+f_j-c_nt_j+c_nS+c_jt_j-c_jS\}\)

然后就很斜率优化了。。

可是\(t_i\)并没有单调性

所以不能直接单调队列优化。可以用一个单调栈维护凸包,然后查询的时候就在上面二分即可。

code

/*
* @Author: wxyww
* @Date:   2019-12-17 19:59:58
* @Last Modified time: 2019-12-17 21:13:05
*/
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<ctime>
using namespace std;
typedef long long ll;
const int N = 300100;
ll read() {
	ll x = 0,f = 1;char c = getchar();
	while(c < '0' || c > '9') {
		if(c == '-') f = -1; c = getchar();
	}
	while(c >= '0' && c <= '9') {
		x = x * 10 + c - '0'; c = getchar();
	}
	return x * f;
}
ll c[N],t[N],n;
ll S;

struct node {
	ll k,b;
}q[N];

int top;

void ins(ll k,ll b) {
	while(top >= 1) {
		ll k2 =  q[top].k,k3 = q[top - 1].k,b2 = q[top].b,b3 = q[top - 1].b;
		if((b2 - b) * (k2 - k3) <= (b3 - b2) * (k - k2)) --top;
		else break;
	}
	q[++top] = (node){k,b};
}
inline ll calc(ll k,ll b,ll x) {
	return k * x + b;
}
ll get(ll x) {
	int l = 1,r = top;
	int ans = 0;
	while(l <= r) {
		int mid = (l + r) >> 1;
		if(calc(q[mid].k,q[mid].b,x) > calc(q[mid - 1].k,q[mid - 1].b,x)) r = mid - 1;
		else ans = mid,l = mid + 1;
	}
	return calc(q[ans].k,q[ans].b,x);
}

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();
	}
	q[0].k = c[n],q[0].b = S * c[n];
	ins(c[n],S * c[n]);
	ll now = 0;
	for(int i = 1;i <= n;++i) {
		now = get(t[i]);
		ins(c[n] - c[i],now + c[i] * t[i] - c[n] * t[i] + S * (c[n] - c[i]));
	}
	cout<<now;
	return 0;
}
posted @ 2019-12-17 21:48  wxyww  阅读(109)  评论(0编辑  收藏  举报