moiezen(2018.10.16)

题意:有n件行李,编号为1~n。行李的质量是模 P 意义下的(P不一定是质数)。有 k 个背包,要装下这些行李,为了方便在背包中找行李,每个背包中的行李编号是连续的,允许有背包为空。我们想让最重的背包尽量轻。你可以选择一个x(0<=x<P),给所有行李的质量+x 并%P,然后才装到背包里去。 你要先选择一个 x,再选择一种行李分配方案,使得最重的背包尽量轻。(n<=10000,P<=10000)

题解:暴力做法直接枚举x,二分答案即可,时间复杂度\(O(nPlogn)\)
满分做法就是在暴力基础上加上一个random_shuffle,考虑对x枚举的顺序随机一下,这样枚举到一个x时候,可以先去check一下是否比目前的最优解要优,如果是,那么再去二分,否则直接continue。
由于一个随机排列中比前面所有数都大的数的数量期望为log,所以复杂度为\(O(nP+nlognlogP)\)

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int n,p,k,a[10001],b[10001],c[10001],mx;long long ans;
bool check(int x)
{
    if(mx>x)return 0;
    int sum=c[1],now=1;
    for(int i=2;i<=n;i++)
    {
        sum+=c[i];
        if(sum>x)
        {
            now++;
            if(now>k)return 0;
            sum=c[i];
        }
    }
    return 1;
}
int main()
{
    scanf("%d%d%d",&n,&p,&k);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]),ans+=a[i];
    for(int j=0;j<p;j++)b[j]=j;
    random_shuffle(b+1,b+p+1);
    for(int i=1;i<=p;i++)
    {
        int now=b[i];mx=0;
        for(int j=1;j<=n;j++)c[j]=(a[j]+now)%p,mx=max(mx,c[j]);
        if(!check(ans))continue;
        int l=0,r=ans;
        while(l<r)
        {
            int mid=(l+r)>>1;
            if(check(mid))r=mid;
            else l=mid+1;
        }
        ans=r;
    }
    printf("%lld\n",ans);
}
posted @ 2018-10-17 16:19  蒟蒻--lichenxi  阅读(308)  评论(0编辑  收藏  举报