【摆渡车】
调自闭了
记得那是\(Day1\)的晚上,我和最强的yem在看pj的题
就发现了这道
之后随手推了一个斜率优化,被杨神嘲讽了
结果发现斜率优化确实是最好的做法了
我们根据时间轴来\(dp\)
可以把题意转化为数轴上有很多个点,现在将数轴划分成若干个区间,最小化每一个点到其所在区间右端点的距离
之后就可以搞出斜率优化的方程了
\[dp[i]=dp[j]+i*(pre[i]-pre[j])-(s[i]-s[j])\ (i-j>=m)
\]
显然这个方程很好转移
但是由于\(pre\)很多时候取值是相同的,很多时候会没有斜率
非常棘手啊,于是显然对于两个坐标相同的点应该保留较低的那个点
在判断斜率的时候体现这一点就好了
代码
// luogu-judger-enable-o2
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define re register
#define maxn 4010005
#define LL long long
#define X(i) (pre[i])
#define Y(i) (dp[(i)]+s[(i)])
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define D double
inline int read()
{
char c=getchar();
int x=0;
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();
return x;
}
int pre[maxn],s[maxn];
int dp[maxn];
int n,m,T;
int a[1001];
int q[maxn],h,t;
inline double K(int i,int j)
{
return (D)(Y(j)-Y(i))/(D)(X(j)==X(i) ? 1e-9:X(j)-X(i));
}
int main()
{
n=read();m=read();
for(re int i=1;i<=n;i++) a[i]=read();
std::sort(a+1,a+n+1);
for(re int i=1;i<=n;i++)
pre[a[i]]++,s[a[i]]+=a[i];
T=a[n]+m;
for(re int i=1;i<=T;i++) pre[i]+=pre[i-1],s[i]+=s[i-1];
h=1,t=0;
for(re int i=0;i<=T;i++)
{
if(i-m>=0)
{
while(h<t&&K(q[t-1],q[t])>=K(q[t-1],i-m)) t--;
q[++t]=i-m;
}
while(h<t&&K(q[h],q[h+1])<=i) h++;
dp[i]=i*pre[i]-s[i];
if(h<=t)dp[i]=min(dp[q[h]]+i*(pre[i]-pre[q[h]])-s[i]+s[q[h]],dp[i]);
}
int ans=1e9+7;
for(re int i=T-m;i<=T;i++) ans=min(ans,dp[i]);
printf("%d\n",ans);
return 0;
}