[BZOJ2726][SDOI2012]任务安排(DP+凸壳二分)

2726: [SDOI2012]任务安排

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 1580  Solved: 466
[Submit][Status][Discuss]

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

HINT

Source

[ Submit][ Status][ Discuss]

复习了无数次CDQ分治和斜率优化,CDQ分治真的是博大精深。

但是,这道题,要个鬼CDQ分治啊!询问斜率不单调又不是插入点横坐标不单调!直接二分找切点不就好了!

说下思路吧,首先$O(n^3)$的DP谁都想得出来,考虑优化,f[i]记录前i个物品的信息。

但是问题来了,时间有后效性,前面的总时间对后面有影响,除非多设一维时间状态,而这样又是$n^3$的了,那么我们干脆就考虑前i个物品的总费用以及这i个物品给后面所有物品带来的费用之和。显然这样是满足无后效性,同时也满足最优子结构的。

设F[i]=f[i]前缀和,T[i]同理,则有DP方程:$dp[i]=min\{dp[j]+(T[i]-T[j]+S)(F[n]-F[j])\}$。这样复杂度就变为$O(n^2)$了。

熟练的选手一眼就知道这是斜率优化的形式,复杂度立刻降为$O(n)$。

但是!看Discuss知道这题的时间可以是负的!于是网上几乎所有的题解立刻全部变为CDQ分治版本,但其实并不需要,因为插入的点的横坐标仍然是单调的所以并不需要动态维护凸壳。询问斜率不单调的话直接二分找直线和凸壳的切点即可。这一点也是这题和货币兑换Cash的一个本质区别。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define rep(i,l,r) for (int i=l; i<=r; i++)
 4 typedef long long ll;
 5 using namespace std;
 6 
 7 const int N=1000010;
 8 ll T[N],F[N],f[N];
 9 int n,s,S,st,ed,q[N];
10 
11 ll Y(int j){ return f[j]-F[n]*T[j]+F[j]*T[j]-F[j]*S; }
12 
13 void dp(){
14     st=ed=0;
15     rep(i,1,n){
16         int l=0,r=ed-1,ans=ed;
17         while (l<=r){
18             ll mid=(l+r)>>1;
19             if (1ll*(F[q[mid+1]]-F[q[mid]])*T[i]<=Y(q[mid+1])-Y(q[mid])) ans=mid,r=mid-1; else l=mid+1;
20         }
21         int j=q[ans]; f[i]=f[j]+(F[n]-F[j])*(T[i]-T[j]+S);
22         while (st<ed && 1ll*(Y(q[ed])-Y(q[ed-1]))*(F[i]-F[q[ed]])>=(Y(i)-Y(q[ed]))*(F[q[ed]]-F[q[ed-1]])) ed--;
23         q[++ed]=i;
24     }
25 }
26 
27 int main(){
28     freopen("bzoj2726.in","r",stdin);
29     freopen("bzoj2726.out","w",stdout);
30     scanf("%d%d",&n,&S);
31     rep(i,1,n) scanf("%lld%lld",&T[i],&F[i]),T[i]+=T[i-1],F[i]+=F[i-1];
32     dp(); printf("%lld\n",f[n]);
33     return 0;
34 }

 

转载于:https://www.cnblogs.com/HocRiser/p/8681536.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值