链接:

P5785

弱化版:P2365


题意:

有 \(n\) 个任务待完成,每个任务有一个完成时间 \(t_i\) 和费用系数 \(f_i\),相邻的任务可以被分成一批。从零时刻开始这些任务会被机器分批完成,在每批任务开始前机器有一个给定启动时间 \(s\),一批任务的完成时间是这批任务完成时间之和,同一批任务视作在同一时刻完成。

每个任务的费用是他的完成时刻和费用系数的乘积,请最小化总费用。


分析:

如果设 \(dp[i]\) 为第 \(i\) 个任务作为当前这一批任务的最后一个时的最优解,这样会很麻烦,因为会涉及到 "此时一共有多少批" 这个很麻烦的状态。这里有一个dp的trick费用提前

我们发现把第 \(i\) 个任务作为当前这一批任务的最后一个时,当前批内以及后面的任务都会多一个 \(s\cdot f_i\) 的费用,于是把 \(dp[i]\) 改为第 \(i\) 个任务作为当前这一批任务的最后一个时,加上后面任务的已有贡献的最优解。可以联系状态转移理解:

\[dp[i]=dp[j]+(\sum_{k=j+1}^if_i\times\sum_{k=1}^it_i)+s\sum_{k=j+1}^nf_i
\]

于是设 \(sumf[i]=\sum\limits_{k=1}^if_k\),\(sumt[i]=\sum\limits_{k=1}^it_k\)

\[dp[i]=dp[j]+(sumf[i]-sumf[j])\times sumt[i]+s(sumf[n]-sumf[j])
\]

于是

\[dp[i]=dp[j]+sumf[i]sumt[i]-sumf[j]sumt[i]+s\cdot sumf[n]-s\cdot sumf[j]
\]

依套路:

\[(dp[j]-s\cdot sumf[j])=sumt[i]sumf[j]+(dp[i]-sumf[i]sumt[i]-s\cdot sumf[n])
\]

于是求斜率为 \(sumt[i]\),过点 \((sumf[j],dp[j]-s\cdot sumf[j])\) 的直线的最小截距。

这里的 \(sumt[i]\) 并不是单调的,所以不能用单调队列优化,一个优秀的办法是二分找到第一个与下一个点之间的斜率大于 \(sumt[i]\) 的点,时间复杂度 \(O(n\log n)\)。


算法:

单调队列维护下凸包,然后每次二分找到最优决策点,根据其信息得到 \(dp[i]\),继续维护凸包即可。时间复杂度 \(O(n\log n)\)。


重要的细节:

这道题让我注意到了一个斜率优化dp中的一个很重要的细节,就是维护下凸包的判断。

这道题在洛谷讨论区也有不少人曾发起过讨论,称因为精度问题而必须把判断斜率改成乘法,但其实并不是这样一个简单的借口,我们来看下面三个代码,他们除了维护下凸包的判断没有任何区别。

(Y(q[qt])-Y(q[qt-1]))*(X(i)-X(q[qt])) >= (Y(i)-Y(q[qt]))*(X(q[qt])-X(q[qt-1])) 100pts

slope(q[qt],q[qt-1])>=slope(i,q[qt]) 80pts

slope(q[qt],q[qt-1])>=slope(i,q[qt-1]) 100pts

显然,这并不是精度的问题,甚至这三种写法都是错的!

我们来看下面这种情况:(这种情况虽然在数据上比较难构造,但单纯考虑维护下凸包来看还是很常见的)

这样三个横坐标相等点从上到下按 \(1,3,2\) 的次序被加入下凸包的维护。

显然,根据人脑判断,这样的数据要维护下凸包自然是保留 \(2\) 号点,弹出 \(3\) 号点,但普遍的写法是不存在 "弹出当前点" 这种操作的,只有 "弹出上一个点"。于是:

如果是第一种判断,两边的乘积都是 \(0\),于是会弹出 \(2\) 号点,此时 \(1,3\) 点间斜率为 -inf,后面的点可以加入,但在局部确实出现了正确性的错误。

如果是第二种判断,由于 \(1,2\) 间斜率是 -inf,\(2,3\) 点间斜率是 +inf,所以不会有点弹出,此时 \(2,3\) 点之间斜率为 +inf,这种情况下,后面的点根本无法加入下凸包的维护了,出现了严重的问题。

如果是第三种判断,由于 \(1,2\) 间斜率是 -inf,\(1,3\) 点间斜率是 -inf,这里也涉及取等的问题,如果取等,那么会弹出 \(2\) 号点,变成第一种情况;如果不取等,那么 \(2\) 号点不会被弹出,于是变成第二种情况。

所以这题错误的真正原因根本不是什么精度,而是维护下凸包的做法在存在横坐标相等的情况下本来就是假的!

根据这题横坐标非严格单调增,一个感觉比较正确的做法是在维护下凸包时先判断与前面的点横坐标是否相等,如果相等,保留纵坐标最低的点;如果不等,按照原本的做法即可。

在这样的做法下,不等号是否取等是没有正确性的影响的。取等会弹出一些无用的点,可能会快几毫秒。

所以在有横坐标相等的情况下,一个正确的写法是:

if(X(i)==X(q[qt])&&1<qt){if(Y(i)<Y(q[qt]))qt--;else continue;}
while(1<qt&&slope(q[qt],q[qt-1])>=slope(i,q[qt]))qt--;
q[++qt]=i;

这样写的话,不管判断是用的乘法还是斜率,哪两个斜率判断,或者取不取等,都是能过的。不排除真的会有精度错误,但我认为不能把根据观察发现除法比乘法劣的情况统一归咎到精度错误上。


代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define in read()
inline int read(){
int p=0,f=1;
char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){p=p*10+c-'0';c=getchar();}
return p*f;
}
#define X(x) (sumf[x])
#define Y(x) (dp[x]-sumf[x]*s)
#define dx(x,y) (X(x)-X(y))
#define dy(x,y) (Y(x)-Y(y))
#define slope(x,y) ((double)dy(x,y)/dx(x,y))
const int N=3e5+5;
const int inf=0x7fffffffffffffff;
int n,s,t[N],f[N],sumt[N],sumf[N],dp[N],q[N],qt;
inline int bin(int key){
int l=1,r=qt,mid;
while(l<r){
mid=(l+r)>>1;
double cp=(mid==qt)?inf:slope(q[mid+1],q[mid]);
if(cp<key)l=mid+1;
else r=mid;
}
return l;
}
signed main(){
n=in,s=in;
for(int i=1;i<=n;i++)
t[i]=in,f[i]=in,
sumt[i]=sumt[i-1]+t[i],
sumf[i]=sumf[i-1]+f[i];
qt=1;
for(int i=1;i<=n;i++){
int qh=bin(sumt[i]);
dp[i]=Y(q[qh])-sumt[i]*X(q[qh])+sumf[i]*sumt[i]+sumf[n]*s;
if(X(i)==X(q[qt])&&1<qt){if(Y(i)<Y(q[qt]))qt--;else continue;}
while(1<qt&&slope(q[qt],q[qt-1])>slope(i,q[qt-1]))qt--;
q[++qt]=i;
}
cout<<dp[n];
return 0;
}

题外话:

这个重要的细节确实让我对斜优里维护下凸包部分的印象深了许多。

洛谷 P5785 [SDOI2012] 任务安排的更多相关文章

  1. 洛谷P2751[USACO]工序安排

    题目传送门 怎么说呢,这个题目我刚开始随便乱搞了几下,交了个暴力代码上去居然还水了49分,数据确实有点弱啊,然后看到洛谷上那位大佬Redbag的题解瞬间就佩服的五体投地,那真的是简洁.易懂又高效.直接 ...

  2. 洛谷P2365/5785 任务安排 题解 斜率优化DP

    任务安排1(小数据):https://www.luogu.com.cn/problem/P2365 任务安排2(大数据):https://www.luogu.com.cn/problem/P5785 ...

  3. 洛谷 P2303 [SDOi2012]Longge的问题 解题报告

    P2303 [SDOi2012]Longge的问题 题目背景 SDOi2012 题目描述 Longge的数学成绩非常好,并且他非常乐于挑战高难度的数学问题.现在问题来了:给定一个整数\(N\),你需要 ...

  4. 洛谷 P2498 [SDOI2012]拯救小云公主 解题报告

    P2498 [SDOI2012]拯救小云公主 题目描述 英雄又即将踏上拯救公主的道路-- 这次的拯救目标是--爱和正义的小云公主. 英雄来到\(boss\)的洞穴门口,他一下子就懵了,因为面前不只是一 ...

  5. 洛谷P2303 [SDOi2012]Longge的问题

    题目背景 SDOi2012 题目描述 Longge的数学成绩非常好,并且他非常乐于挑战高难度的数学问题.现在问题来了:给定一个整数N,你需要求出∑gcd(i, N)(1<=i <=N). ...

  6. 洛谷 P2837 晚餐队列安排

    P2837 晚餐队列安排 题目背景 Usaco Feb08 Bronze 题目描述 为了避免餐厅过分拥挤,FJ要求奶牛们分2批就餐.每天晚饭前,奶牛们都会在餐厅前排队入内,按FJ的设想,所有第2批就餐 ...

  7. 洛谷P2303 [SDOi2012] Longge的问题 数论

    看懂了题解,太妙了TT但是想解释的话可能要很多数学公式打起来太麻烦了TT所以我就先只放代码具体推演的过程我先写在纸上然后拍下来做成图片放上来算辣quq 好的那我先滚去做题了做完这题就把题解放上来.因为 ...

  8. 洛谷P2351 [SDOi2012]吊灯 【数学】

    题目 Alice家里有一盏很大的吊灯.所谓吊灯,就是由很多个灯泡组成.只有一个灯泡是挂在天花板上的,剩下的灯泡都是挂在其他的灯泡上的.也就是说,整个吊灯实际上类似于[b]一棵树[/b].其中编号为 1 ...

  9. 洛谷P2498 [SDOI2012]拯救小云公主 【二分 + 并查集】

    题目 英雄又即将踏上拯救公主的道路-- 这次的拯救目标是--爱和正义的小云公主. 英雄来到boss的洞穴门口,他一下子就懵了,因为面前不只是一只boss,而是上千只boss.当英雄意识到自己还是等级1 ...

随机推荐

  1. Jetpack Compose学习(4)——Image(图片)使用及Coil图片异步加载库使用

    原文地址 Jetpack Compose学习(4)--Image(图片)使用及Coil图片异步加载库使用 | Stars-One的杂货小窝 本篇讲解下关于Image的使用及使用Coil开源库异步加载网 ...

  2. grep、cut、awk、sed的使用

    grep.cut.awk.sed 常常应用在查找日志.数据.输出结果等等,并对我们想要的数据进行提取.通常grep,sed命令是对行进行提取,cut跟awk是对列进行提取 处理海量数据之grep命令 ...

  3. CodeForce-803B Distances to Zero(贪心DP)

    Distances to Zero CodeForces - 803B 题意:给定一个数列 a0, a1, ..., an - 1.对于数列中的每一项都要求出与该项最近的0与该项的距离.保证数列中有至 ...

  4. 使用Java api对HBase 2.4.5进行增删改查

    1.运行hbase 2.新建maven项目 2.将hbase-site.xml放在项目的resources文件夹下 3.修改pom.xml文件,引入hbase相关资源 <repositories ...

  5. Dubbo 学习(二)服务注册与发现

    在上一篇中我们提到,dubbo官网上推荐使用ZooKeeper作为注册中心.那么今天我们就通过代码来实践一番,看看一个dubbo的服务消费者如果找到通过ZooKeeper暴露自己的dubbo服务提供者 ...

  6. Jemter请求乱码解决方案

    1:jemeter查看结果树乱码 (1)在jmeter的bin目录下找到jmeter.properties这个文件,添加上 sampleresult.default.encoding=utf-8 (2 ...

  7. P4643-[国家集训队]阿狸和桃子的游戏【结论】

    正题 题目链接:https://www.luogu.com.cn/problem/P4643 题目大意 给出\(n\)个点\(m\)条边的无向图,两个人轮流选择一个未被选择的点加入点集. 然后每个人的 ...

  8. CF585E-Present for Vitalik the Philatelist【莫比乌斯反演,狄利克雷前缀和】

    正题 题目链接:https://www.luogu.com.cn/problem/CF585E 题目大意 给出一个大小为\(n\)的可重集\(T\),求有多少个它的非空子集\(S\)和元素\(x\)满 ...

  9. 1-SQL Server2019安装

    sql server2019安装 首先去官网下载(下载express版本): 打开安装程序 选择自定义 更改一下安装目录,点击安装 等待安装 等安装完成后,出现如下页面 选择SQL Server独立安 ...

  10. 解决vue项目中遇到父组件的按钮或操作控制重新挂载子组件但是子组件却无效果的情况

    在vue项目中终会遇到需要父组件的按钮或操作控制重新挂载子组件的需求,我在新项目中就遇到这种需求.真实场景是父组件的早,中,晚三个按钮(代表三个时间段)来控制子组件的table表格列的动态加载. 子组 ...