题解 [BZOJ4368][IOI2015]boxes纪念品盒
解析
可以发现,发纪念品有三种方式:
- 从左边走再原路返回.
- 从右边走再原路返回.
- 走一圈.
注意到,第三种走法最多只会走一次,
因为如果走了多次,那发放的物品数量就会>=\(2k\),
那么一定有半边的数量>=\(k\).
因此就可以转化为一次1/2操作加一次3操作(先发掉\(k\)个).
那么我们可以先DP一下1/2操作发前\(i\)个纪念品所用的时间\(f1[i]/f2[i]\),
那没有3操作的时间就是f1+f2,
考虑走一圈的情况,枚举从左边走发的纪念品数更新答案即可.
code:
#include <iostream>
#include <cstdio>
#include <cstring>
#define int long long
#define fre(x) freopen(x".in","r",stdin),freopen(x".out","w",stdout)
using namespace std;
inline int read(){
int sum=0,f=1;char ch=getchar();
while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0' && ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
return f*sum;
}
const int N=10000005;
int n,K,L,p[N];
int st1[N],st2[N],tp1,tp2;
int f1[N],f2[N];
signed main(){
n=read();K=read();L=read();
for(int i=1;i<=n;i++){
p[i]=read();
if(p[i]<=L>>1) st1[++tp1]=p[i];
else st2[++tp2]=L-p[i];
}
for(int i=1;i<=tp2>>1;i++) swap(st2[i],st2[tp2-i+1]);
for(int i=1;i<=tp1;i++)
f1[i]=(i<=K? st1[i]:st1[i]+f1[i-K]);
for(int i=1;i<=tp2;i++)
f2[i]=(i<=K? st2[i]:st2[i]+f2[i-K]);
int ans=(f1[tp1]+f2[tp2])<<1;
for(int i=tp1-K;i<=tp1;i++){//至少要从tp1开始枚举不然发不完
ans=min(ans,((f1[i]+f2[max((int)0,n-K-i)])<<1)+L);
}
printf("%lld\n",ans);
return 0;
}