【摆渡车】

调自闭了

记得那是\(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;
}

posted @ 2019-01-02 12:19  asuldb  阅读(158)  评论(2编辑  收藏  举报