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;
}
标签:截距,ch,int,Luogu,P2365,times,斜率,read,安排 来源: https://www.cnblogs.com/NLDQY/p/11264904.html