记录编号 573199 评测结果 AAAAAAAAAAAAAAAAAAAA
题目名称 [BZOJ 2726]任务安排3 最终得分 100
用户昵称 Gravataryuan 是否通过 通过
代码语言 C++ 运行时间 1.815 s
提交时间 2022-07-21 17:52:26 内存使用 11.91 MiB
显示代码纯文本
/*
斜率优化+二分查找  时间复杂度:O(N*logN)  

与 任务安排2(cogs 1162) 一题不同的是,T可能是负数,意味着sumT不再具有单调性,
从而需要最小化截距的直线斜率 S+sumT[i] 不具有单调性。故,不能在单调队列中
只保留凸壳上“ 连接相邻两点的线段斜率 ”大于 S+sumT[i] 的部分,而是必须维护整个凸壳。
这样一来,就不需要在队头把斜率与 S+sumT[i] 比较。

队头也不一定是最优决策,我们可以在单调队列中二分查找,求一个位置 p,p左侧线段斜率比 S+sumT[i] 
小,右侧斜率比 S+sumT[i] 大,p就是最优决策。

队尾维护斜率单调性的方法与 任务安排2 一题相同。
*/
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N=300001;

ll n,s,f[N],sumT[N],sumC[N];
ll q[N],l=1,r=1;//队列

int binary_search(int i,int k)
{
	if(l==r) return q[l];
	int L=l,R=r;
	while(L<R)
	{
		int mid = (L+R) / 2;
		if ((f[q[mid+1]]-f[q[mid]]) <= k*(sumC[q[mid+1]]-sumC[q[mid]]))
			L=mid+1;
		else
			R=mid;
	}
	return q[L];
}

int main()
{
	freopen("batch1.in","r",stdin);
	freopen("batch1.out","w",stdout);
	memset(f,0x3f,sizeof(f));
	memset(sumT,0,sizeof(sumT));
	memset(sumC,0,sizeof(sumC));
	memset(q,0,sizeof(q));
	cin>>n>>s;
	for(int i=1;i<=n;i++)
	{
		cin>>sumT[i]>>sumC[i];
		sumT[i] += sumT[i-1];//前缀和
		sumC[i] += sumC[i-1];
	}
	f[0]=0;
	q[1]=0;
	for(int i=1;i<=n;i++)
	{//队列维护整个凸壳,通过二分找到最优决策
		//因为 S+sumT[i] 不再具有单调性,故队头不再需要保持单调性的检查
		
		//在队列中二分查找最优决策,执行状态转移,计算f[i];
		int j=binary_search(i,s + sumT[i]);
		f[i]=f[j]-(s+sumT[i])*sumC[j]+sumT[i]*sumC[i]+s*sumC[n];//转移
		
		/*
		把新决策 i 插入队尾,插入之前,若3个决策点j1=q[r-1],j2=q[r],j3=i
		不满足斜率单调递增(不满足下凸性,即 j2 是无用决策),则直接从队尾将
		q[r]出队,继续检查新队尾。
		*/
		while(l<r && (f[q[r]]-f[q[r-1]])*(sumC[i]-sumC[q[r]])>=(f[i]-f[q[r]])*(sumC[q[r]]-sumC[q[r-1]]))
		//斜率不等式两边同乘以原来两斜率分母之积,避免大数除法影响精度。
			r--;
		q[++r]=i;//决策 i 入队
	}
	cout<<f[n]<<endl;
	return 0;
}