题解 [BZOJ4368][IOI2015]boxes纪念品盒

题面

解析

可以发现,发纪念品有三种方式:

  1. 从左边走再原路返回.
  2. 从右边走再原路返回.
  3. 走一圈.

注意到,第三种走法最多只会走一次,

因为如果走了多次,那发放的物品数量就会>=(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
",ans);
	return 0;
}

原文地址:https://www.cnblogs.com/zsq259/p/11416046.html