[NOIp2018] 摆渡车

普及组考斜率优化

考虑 d p dp dp

f i f_i fi表示在 i i i时刻发车的最小等待时间

那么我们可以得到转移方程

f i = min ⁡ 0 ≤ j ≤ i − m { f j + ( c n t i − c n t j ) i − ( s u m i − s u m j ) } f_i=\min\limits_{0\leq j\leq i-m} \{f_j+(cnt_i-cnt_j)i-(sum_i-sum_j)\} fi=0jimmin{fj+(cnticntj)i(sumisumj)}

其中 c n t , s u m cnt,sum cnt,sum分别是学生个数和学生到达时间的前缀和

发现 j j j的最优转移一定是在 [ i − 2 m , i − m ] [i-2m,i-m] [i2m,im]上的,这样复杂度可以变成 O ( t m ) O(tm) O(tm)

但是众所周知CCF老爷机的速度

我们发现这个式子可以斜率优化,写成

f j + s u m j = i × c n t j − c i × i + s i + f i f_j+sum_j=i\times cnt_j-c_i\times i+s_i+f_i fj+sumj=i×cntjci×i+si+fi

其中 y = f j + s u m j , k = i , x = c n t j , b = − c i × i + s i + f i y=f_j+sum_j,k=i,x=cnt_j,b=-c_i\times i+s_i+f_i y=fj+sumj,k=i,x=cntj,b=ci×i+si+fi

然后这道题就愉快的 O ( t ) O(t) O(t)解决了!

#include <bits/stdc++.h>
using namespace std;

# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)

typedef long long ll;

const int N=4e6+5;
const int mod=1e9;

template<typename T> void read(T &x){
   x=0;int f=1;
   char c=getchar();
   for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
   for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
    x*=f;
}

int n,m,top;
int a[N];
int cnt[N],sum[N];
int f[N];
int q[N],head,tail;
int ans=INT_MAX;

int fz(int x,int y){
	return f[x]+sum[x]-f[y]-sum[y];	
}

int fm(int x,int y){
	return cnt[x]-cnt[y];	
}

int main()
{
	read(n),read(m);
	Rep(i,1,n)read(a[i]),sum[a[i]]+=a[i],cnt[a[i]]++;
	Rep(i,1,n)top=max(top,a[i]);
	Rep(i,1,top+m)sum[i]+=sum[i-1],cnt[i]+=cnt[i-1];
	head=1,tail=0;
	Rep(i,0,m-1)f[i]=cnt[i]*i-sum[i];
	q[++tail]=0;
	Rep(i,m,top+m){
		while(head<tail&&fz(q[head+1],q[head])<=1ll*i*fm(q[head+1],q[head]))head++;
		f[i]=f[q[head]]+(cnt[i]-cnt[q[head]])*i-(sum[i]-sum[q[head]]);
		while(head<tail&&1ll*fz(q[tail],1ll*q[tail-1])*fm(i-m+1,q[tail])>=1ll*fz(i-m+1,q[tail])*fm(q[tail],q[tail-1]))tail--;
		q[++tail]=i-m+1;
	}
	Rep(i,top,top+m)ans=min(ans,f[i]);
	printf("%d\n",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值