IOI2015 boxes纪念品盒

BZOJ 4368: [IOI2015]boxes纪念品盒

BZOJ传送门

Description

IOI2015开幕式正在进行最后一个环节。按计划在开幕式期间,每个代表队都将收到由主办方发放的一个装有纪念品的盒子。然而所有志愿者都被精彩的开幕式所吸引,除Aman外其他人完全忘记了发放纪念品这件事。Aman是一位热情的志愿者,为使得IOI尽量圆满,他要用最短的时间将所有纪念品发放出去。

开幕式的场地是一个圆环,被分为 个完全相等的区域,这些区域的编号依次为 到,也就是说,对于0≤i≤L-2,区域i与区域i+1相邻,且区域L-1与区域0相邻。场地上共有N个代表队,每队坐在上面的一个区域上,每个区域可以包含任意多个代表队,也可以为空。

共有N个相同的纪念品。开始,Aman和所有纪念品都在区域0。Aman应该给每队一个纪念品,并且在发放完最后一个纪念品后他必须回到区域0。注意,有些队可能坐在区域0。

在任意时刻,Aman只能够携带至多K个纪念品。Aman必须从区域0取走这些纪念品,且取纪念品不需要时间。纪念品一旦从区域0被取走后,Aman只能将其发放给某个代表队或者随身携带。无论何时,Aman携带一个或更多的纪念品到达一个这样的区域,该区域有一个代表队尚未收到纪念品,Aman便可将他携带的一个纪念品发给这个代表队。这种发放也在瞬间完成。他所花的时间都消耗在区域之间的移动上。无论携带多少纪念品,Aman都需要1秒钟从一个区域移动到其相邻的区域(可以顺时针移动也可以逆时针移动)。

你的任务是计算出Aman发放完所有纪念品并返回到他的最初区域所需要的最短时间(秒数)。

Input

第一行: N K L

第二行: positions[0] … positions[N-1]

N: 代表队的数目。

K: Aman在同一时间能够携带纪念品的最大数目。

L: 开幕式场地上的区域数目。

positions: 一个长度为N的数组,positions[0],...,positions[N-1]给出了所有代表队所在区域的编号。positions 的元素按非递减排序。

Output

一个整数,表示Aman 能够完成这一任务所需的最短时间(秒数)。

Sample Input

3 2 8
1 2 5

Sample Output

10

HINT

img

题解:

2019.10.31 IOI模拟赛T1(滑稽)爆零场

一开始想到的是贪心,但是贪心策略一直是错的,思路也不是很清晰,通过特判拿了25分但是数据强一点还是爆零。

还是感谢一下出题人吧@EM-LGH

正解思路:

很容易发现这样一个走路方式:

对于这个圈,我们可以向左走几个点,原路返回。也可以向右走几个点然后返回。这是我在考场上想到的。但我忽略了一种情况:也可以走一圈回来(就是向左/向右走几个点之后不原路返回直接从另一个方向回去)。

那么剩下的是我们的决策过程。我们发现,走一圈这种决策只能使用一次,因为走两圈一共会分发\(2k\)个纪念品,根据抽屉原理,左边和右边肯定会有一侧\(\ge k\),那么就不是最优决策。

那么,贪心一下,先用原路返回的方式处理一下比较优的抉择,也就是离零点比较近的左右点。然后再走一圈完事。

代码:

#include<cstdio>
#include<algorithm>
#define int long long
using namespace std;
const int maxn=1e7+10;
int n,k,l;
int sl[maxn],sr[maxn],cntl,cntr;
int fl[maxn],fr[maxn];
signed main()
{
    scanf("%lld%lld%lld",&n,&k,&l);
    for(int i=1;i<=n;i++)
    {
        int x;
        scanf("%lld",&x);
        if(x<=l/2)
            sl[++cntl]=x;
        else
            sr[++cntr]=l-x;
    }
    for(int i=1;i<=cntr && i<=cntr-i+1;i++)
        swap(sr[i],sr[cntr-i+1]);
    for(int i=1;i<=cntl;i++)
    {
        if(i<=k)
            fl[i]=sl[i];
        else
            fl[i]=fl[i-k]+sl[i];
    }
    for(int i=1;i<=cntr;i++)
    {
        if(i<=k)
            fr[i]=sr[i];
        else
            fr[i]=fr[i-k]+sr[i];
    }
    int ans=(fl[cntl]+fr[cntr])<<1;
    for(int i=cntl-k;i<=cntl;i++)
        ans=min(ans,(fl[i]+fr[max(cntr-k+cntl-i,0ll)])*2+l);
    printf("%lld",ans);
    return 0;
}
posted @ 2019-10-31 15:18  Seaway-Fu  阅读(318)  评论(0编辑  收藏  举报