[BZOJ2726][SDOI2012]任务安排

bzoj
luogu(洛谷上面的是个\(O(n^2)\)题)

description

\(n\)个任务,每次可以同时处理一段连续的任务,这些任务将同时处理完,且所需时间等于这些任务单独处理的总时间。每次处理任务前,机器需要\(S\)的时间准备。每个任务还有一个费用系数,这个任务的费用等于它的完成时刻乘上费用系数。求最小代价。
\(n\le3\times10^5\)

sol

怎么\(dp\)算是一个难点了。
直接计算当前这一次处理的所有任务的总代价,那就需要多记一维状态表示之前已经分了多少批。
考虑在每次耗费时间的时候计算这段时间产生的费用,显然这段时间里尚未处理的任务都会产生相应的费用,而尚未处理的任务恰好是一段后缀。
所以有\(dp\)式:

\[f_i=\min\{f_j+(t_i-t_j+S)\times(s_n-s_j)\} \]

其中\(t,s\)分别是时间和费用系数的前缀和。
简单推导可知,当满足$$\frac{(f_j-t_js_n+t_js_j-Ss_j)-(f_k-t_ks_n+t_ks_k-Ss_k)}{s_j-s_k}\le t_i$$且\(k<j\)时,决策\(j\)优于决策\(k\)
直接斜率优化即可。
然后就发现\(WA\)了。为什么呢?因为时间有负数!(别问我怎么可能,也别问我怎么知道的)
也就是说\(t_i\)不一定单调递增。
但是貌似费用系数全是正的,也就是横坐标是满足单调增的,所以维护个下凸壳然后每次决策时二分即可。
复杂度\(O(n\log n)\)

code

被卡精度了。
为什么弹栈的时候要加那个等于号啊,希望dalao们能教教我。

#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
ll gi(){
	ll x=0,w=1;char ch=getchar();
	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if (ch=='-') w=0,ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return w?x:-x;
}
const int N = 1e6+5;
int n,top;ll S,t[N],s[N],f[N];
struct node{int id;ll x,y;}q[N];
ll getdp(int i,int j){
	return f[j]+(t[i]-t[j]+S)*(s[n]-s[j]);
}
int main(){
	n=gi();S=gi();
	for (int i=1;i<=n;++i) t[i]=t[i-1]+gi(),s[i]=s[i-1]+gi();
	for (int i=1;i<=n;++i){
		int l=1,r=top,res=0;
		while (l<=r){
			int mid=l+r>>1;
			if ((q[mid].y-q[mid-1].y)<1ll*t[i]*(q[mid].x-q[mid-1].x)) res=mid,l=mid+1;
			else r=mid-1;
		}
		f[i]=getdp(i,q[res].id);
		node tmp=(node){i,s[i],f[i]-s[n]*t[i]+s[i]*t[i]-s[i]*S};
		while (top&&(q[top].y-q[top-1].y)*(tmp.x-q[top].x)>=(tmp.y-q[top].y)*(q[top].x-q[top-1].x)) --top;
		q[++top]=tmp;
	}
	printf("%lld\n",f[n]);
	return 0;
}
posted @ 2018-07-05 11:52  租酥雨  阅读(436)  评论(1编辑  收藏  举报