bzoj 2726: [SDOI2012]任务安排

12 篇文章 0 订阅
12 篇文章 0 订阅

Description

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

Input

第一行两个整数,N,S。
接下来N行每行两个整数,Ti,Fi。

Output

一个整数,为所求的答案。

Sample Input

5 1
1 3
3 2
4 3
2 3
1 4

Sample Output

153


这题完成的时间是有负数的,但是似乎费用系数Fi没负数。

明明两个输入的描述是一样的。。也不知道出题人在想什么。。或者说毕竟SDOI么

我们考虑从后往前转移 F,T均为后缀和

f[i]=min(f[j]+(Ti-Tj+S)*Fi)

大概这样一个转移式。

假设j<k,j比k优,

即f[j]+(Ti-Tj+S)*Fi<f[k]+(Ti-Tk+S)*Fi

移项后得

f[j]-f[k]<Fi*(Tj-Tk)

因为T不是单调的所以没办法做除法。

我们考虑cdq分治

假设当前区间是[l,r]

则先把[mid,r]的答案处理出来,然后把[mid,r]中的T按照升序排序

这样就保证了T的单调性,暴力维护出凸壳

然后对于[l,mid],因为F是单调的,所以直接顺着凸壳扫过去找到最优转移位置就可以了

#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
struct quest
{
	long long t,f;
	long long fx;
	int p;
}a[300005],b[300005];
inline bool cmp(quest x,quest y)
{
	return x.p<y.p;
}
int q[300005];
long long f[300005];
long long s;
inline bool check(int x,int y,int z)
{
	return (a[z].t-a[y].t)*(a[y].fx-a[x].fx)>=(a[z].fx-a[y].fx)*(a[y].t-a[x].t);
}
inline bool getk(int x,int y,long long z)
{
	return (a[y].fx-a[x].fx)<z*(a[y].t-a[x].t);
}
inline long long calc(int x,int i)
{
	return a[x].fx+a[i].f*(a[i].t-a[x].t+s);
}
inline void solve(int ll,int rr)
{
	if(ll==rr-1)
	{
		return ;
	}
	int mid=(ll+rr)/2;
	solve(mid,rr);
	int i;
	int l=1,r=0;
	for(i=rr-1;i>=mid;i--)
	{
		while(r>1&&check(q[r-1],q[r],i))
			r--;
		r++;
		q[r]=i;
	}
	for(i=mid-1;i>=ll;i--)
	{
		while(l<r&&getk(q[l],q[l+1],a[i].f))
			l++;
		a[i].fx=min(a[i].fx,calc(q[l],i));
	}
	solve(ll,mid);
	int d1=rr-1,d2=mid-1;
	for(i=rr-1;i>=ll;i--)
	{
		if(d1<=mid-1)
		{
			b[i]=a[d2];
			d2--;
		}
		else if(d2<=ll-1)
		{
			b[i]=a[d1];
			d1--;
		}
		else
		{
			if(a[d2].t<a[d1].t||a[d2].t==a[d1].t)
			{
				b[i]=a[d2];
				d2--;
			}
			else
			{
				b[i]=a[d1];
				d1--;
			}
		}
	}
	for(i=ll;i<=rr-1;i++)
		a[i]=b[i];
}
int main()
{
	int n;
	scanf("%d%lld",&n,&s);
	int i;
	for(i=1;i<=n;i++)
	{
		scanf("%lld%lld",&a[i].t,&a[i].f);
		a[i].p=i;
	}
	for(i=n;i>=1;i--)
	{
		a[i].t=a[i+1].t+a[i].t;
		a[i].f=a[i+1].f+a[i].f;
		a[i].fx=a[i].f*(a[i].t+s);
	}
	solve(1,n+1);
	sort(a+1,a+1+n,cmp);
	printf("%lld\n",a[1].fx);
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值