普及组考斜率优化
考虑 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=0≤j≤i−mmin{fj+(cnti−cntj)i−(sumi−sumj)}
其中 c n t , s u m cnt,sum cnt,sum分别是学生个数和学生到达时间的前缀和
发现 j j j的最优转移一定是在 [ i − 2 m , i − m ] [i-2m,i-m] [i−2m,i−m]上的,这样复杂度可以变成 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×cntj−ci×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;
}