Luogu P2365 任务安排

题目链接:Click here

Solution:

我们设\(f[i]\)表示我们把\(i\)单独划分出来后的对总答案的最小代价,\(c[i]\)表示花费的前缀和,\(t[i]\)表示时间的前缀和

\[f[i]=f[j]+t[i]\times (c[i]-c[j])+S\times (c[n]-c[j])\\ f[i]=f[j]+t[i]\times c[i]-t[i]\times c[j]+S\times c[n]-S\times c[j]\\ -f[j]=t[i]\times c[i]-t[i]\times c[j]+S\times c[n]-S\times c[j]-f[i]\\ -f[j]=(-t[i]-S)\times c[j]+t[i]\times c[i]+S\times c[n]-f[i]\\ f[j]=(t[i]+S)\times c[j]-t[i]\times c[i]-S\times c[n]+f[i]\\ \]

我们把这个式子看作形如:\(y=kx+b\)的一次函数,\(c[j]\)看作自变量,则式子的斜率是确定的,截距只有\(f[i]\)不确定

则我们想让截距\(b\)尽量的小,则我们需要用将这个斜率确定的直线向上平移碰到的第一个点(二元组(\(c[j],f[j]\)))来更新

因为这样我们得到的截距才会是最小的,那么我们只要维护一个下凸包就好了

为什么是下凸包呢?考虑选取下凸包中连续三个点\(a_1,a_2,a_3\),由于他是一个下凸包,我们可以得出\(k(a_2,a_1)<k(a_3,a_2)\)

其中\(k(a,b)\)表示\(a,b\)两点连线的斜率。当一个点能更新当前状态时,它一定是下凸包上的点,一个向上凸的点是不可能转移的(可以画图理解)

同时我们注意到它的斜率是单调递增的,所以我们可以维护队首为答案

Code:

#include<bits/stdc++.h>
using namespace std;
const int N=5e3+1;
int n,s,t[N],c[N],f[N];
int head,tail,q[N];
int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
int main(){
	n=read();s=read();
	for(int i=1;i<=n;i++){
		t[i]=read(),c[i]=read();
		t[i]+=t[i-1],c[i]+=c[i-1];
    }
	for(int i=1;i<=n;i++){
		while(head<tail&&(f[q[head+1]]-f[q[head]]<=(t[i]+s)*(c[q[head+1]]-c[q[head]]))) ++head;
		f[i]=f[q[head]]+t[i]*(c[i]-c[q[head]])+s*(c[n]-c[q[head]]);
		while(head<tail&&(f[i]-f[q[tail]])*(c[q[tail]]-c[q[tail-1]])<=(f[q[tail]]-f[q[tail-1]])*(c[i]-c[q[tail]])) --tail;
		q[++tail]=i;
	}printf("%d\n",f[n]);
	return 0;
}

posted @ 2019-07-29 16:53  DQY_dqy  阅读(150)  评论(0编辑  收藏  举报