题目描述
个任务排成一个序列在一台机器上等待完成(顺序不得改变),这
个任务被分成若干批,每批包含相邻的若干任务。从时刻
开始,这些任务被分批加工,第
个任务单独完成所需的时间是
。在每批任务开始前,机器需要启动时间S,而完成这批任务所需的时间是各个任务需要时间的总和(同一批任务将在同一时刻完成)。每个任务的费用是它的完成时刻乘以一个费用系数
。
请确定一个分组方案,使得总费用最小。
数据规模
思路
首先,这还是个很明显的划分类
。
直接上状态:
表示前i个任务分成j批的最小代价。
易得
但是这个状态已经是
级别的了
/-----------华---------丽-----------丽---------的-----------分---------割---------线-----------/
然后,我们会发现限制状态的最大因素就是
。
整个转移中唯一与j有关的就是
因此,我们是否可以想办法将它去掉呢?
考虑在第i个任务结束后重新分一组,那么对第
~
个任务的影响就是加上了
那么,我们就可以将j这维状态直接去掉
状态就变成了:
表示前i个任务的最小代价
转移:
-----------华---------丽-----------丽---------的-----------分---------割---------线-----------
然后,我们又发现,这是个非常经典的 动态规划
显然是无法通过 级别的数据。
考虑优化,对于一道一维状态的 题,优化方向依然只有一个——转移。
(否则把状态优化了不就成了贪心吗?)
如何减少重复或不必要的枚举呢?
设
则对于 的决策, 比 优等价于满足
简单的打开并整理过后就得到了
------------------------------------------------------------------------
但这道题与 不同之处便是 不单调了
但是,仔细思考,这并没有破坏队尾维护的性质,即我们依然可以维护一个单调队列(实则是一个单调栈)
而区别就是现在取值的时候,要在这个单调序列中二分出一个斜率与 最为接近的值
( :写读优的同志们千万别忘了:此题有负数!此题有负数!此题有负数!本人就 了半个小时 )
注:对斜率优化基础还不理解的可以参考斜率优化基础
##代码##
#include<cstdio>
using namespace std;
struct node
{
long long t , c;
}
sum[300005];
long long f[300005];
int q[300005];
long long s;
inline int read()
{
char ch = getchar();
int flag = 1;
while(ch < '0' || ch > '9')
{
if(ch == '-') flag = -1;
ch = getchar();
}
int x = 0;
while(ch >= '0' && ch <= '9') x = x * 10 + ch - 48 , ch = getchar();
return x * flag;
}
inline bool cmp1(int j2 , int j1 , long long k)
{
return f[j2] - f[j1] - s * (sum[j2].c - sum[j1].c) <= k * (sum[j2].c - sum[j1].c);
}
inline bool cmp2(int j3 , int j2 , int j1)
{
return (f[j3] - f[j2] - s * (sum[j3].c - sum[j2].c)) * (sum[j2].c - sum[j1].c) <= (f[j2] - f[j1] - s * (sum[j2].c - sum[j1].c)) * (sum[j3].c - sum[j2].c);
}
inline int find(int l , int r , long long key)
{
while(l < r)
{
int mid = (l + r) >> 1;
if(cmp1(q[mid + 1] , q[mid] , key)) l = mid + 1;
else r = mid;
}
return q[r];
}
int main()
{
int n = read();
s = read();
for(int i = 1;i <= n;i++)
{
int t = read() , c = read();
sum[i].t = sum[i - 1].t + t;
sum[i].c = sum[i - 1].c + c;
}
int head = 1 , tail = 1;
q[1] = 0;
f[0] = 0;
for(int i = 1;i <= n;i++)
{
int loc = find(head , tail , sum[i].t);
f[i] = f[loc] + (s + sum[i].t) * (sum[i].c - sum[loc].c) + (sum[n].c - sum[i].c) * s;
while(head < tail && cmp2(i , q[tail] , q[tail - 1])) tail--;
q[++tail] = i;
}
printf("%lld\n",f[n]);
return 0;
}```