题解 [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\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/zsq259/p/11416046.html