码迷,mamicode.com
首页 > 其他好文 > 详细

题解——慕斯蛋糕

时间:2019-07-13 20:14:54      阅读:97      评论:0      收藏:0      [点我收藏+]

标签:set   搜索引擎   前缀   ons   切分   卡住   class   block   顺序   

慕斯蛋糕

为了避免某些人通过搜索引擎在考试时找到这篇题解,ssw02魔改了一波题面

这是一道触及ssw02感情的题目,ssw02第一次做这道题时,还是一个刚学最短路的boy,然后看着上一届学长YL现场秒切,而自己几乎天天爆0的生活,然让ssw02感到了无助,最后耽搁了WRY学长半个小时才将这道完全超出知识层面的题目解决,这都是9个月前的事了

所以,非常感谢各位能读一下这篇9个月尘封后的题解。

题面

这道题是私有的,所以我魔改了下。

ys今天过生日,一共有N-1个好朋友,加上ys自己就一共N个人,过生日当然要有很好的气氛,所以大家都围成一圈坐在圆桌旁分慕斯蛋糕。ys一共有M个慕斯蛋糕,分慕斯蛋糕时,每个蛋慕斯糕都可以被切成很多很多小块(就当分不完吧),第i个人拿到一个蛋糕并切出自己的分量需要花费时间T[i]。 当一个人切完之后,就会把慕斯蛋糕传到他右手边的第一个人。 M个慕斯蛋糕是同时发出的,每个慕斯蛋糕只能同时被一个人切分。 ys望知道如何分发蛋糕,才能让大家尽快分到慕斯蛋糕,然后开始过生日?
当然ys是一个温柔的人,并不想为难你,所以你只需要输出最后一个人分到慕斯蛋糕的时间即可。

题目思路:

ssw02先B一句,这是一个环,分发的方案就会产生明显的后效性(及时没有环也有后效性),而且说实话,这道题的数据范围对DP极其不友好,n<=50000。
既然正向解题不行,那我们就把题目转化为一个判定性问题

下面是对一些性质的分析:
1.右手边传,及一个人想要拿到蛋糕,必须从他前面的一个人手中传来,每个人拿到蛋糕是具有顺序性的。
2.M个蛋糕同时发出,那么所传达到的人数一定是随时间的增加而增多的,而对于时间的影响只是分配起点的问题。答案满足单调性。
3.从一个固定起点开始,蛋糕的传递只受到总时间的影响和到达人的T[i]的影响。
4.如果一个人在时间T内无法切完蛋糕,那么这个时间绝对不合法。
5.如果第J人在时间T内可以把蛋糕传给第K个人,第K个人在T内可以传给第H个人,那么,在有两个蛋糕的情况下,时间T内 第J—H的人都可以传到蛋糕。

相信到这里,你已经可以想到二分答案了。但是数据仍然卡住了我们,n<=50000. 0<m<=n
好的,答案的单调性也在启示我们,这道题可以倍增。

到这里,思路理清了。
1.破环为链,开二倍数组。
2.二分答案,即二分时间 t 。
3.处理出每个人在t的时间内向右传到最远的人。
4.倍增处理出M个蛋糕的情况,正确性分析中有说明。

AC code 这个代码,是我刚接触OI 3个月时写过的最难代码,但印象也最深刻。 码风和现在完全不一样,见谅

#include<bits/stdc++.h>
const int N=2000005 ;//开2倍数组 
int  pre[N][17],inf=1e9+7;
int  a[N],sum[N];//本值 前缀和都开 2倍 
int  n,m;
inline int read()
{
    int s=0,w=1;
    char g=getchar();
    while(g<'0'||g>'9'){if(g=='-')w*=-1;g = getchar();}
    while(g>='0'&&g<='9'){s = s*10+g-'0';g = getchar();}
    return s*w;
}
bool  check (int lim) 
{
    memset(pre,0,sizeof(pre));
    for (int i=1;i<=n;i++)
        if (a[i]>lim)     //单点超时 false 
            return  false ;
    int k=0;
    for(int i=1;i<=2*n;i++) 
    {                           
            for (;k<i;k++)
            if (sum[i]-sum[k]<=lim) 
                break;              //即求出最大的合法的k值(最远可以一步走多远) 
        pre[i][0]=k; //每个点在1个蛋糕的情况下,最远跑到k的位置 
    }
    for ( int i=1;i<=2*n;i++) 
    {                           
        for(int j=1;j<=15;j++)// 倍增  要处理le9+7 
            pre[i][j]=pre[pre[i][j-1]][j-1];//递推方程 
            //从上一个k的位置再跑2^j-1 
    }
    for(int i=1;i<=2*n;i++) 
    {
        int now=i;
        for(int j=15;j>=0;j--)
            if ((1<<j)&m)        //或运算
                now=pre[now][j];
        if (i-now>=n)
        return true ;
    }
    return false;
}

int dx(int l,int r)
{
    int ans;
    while (l<=r) 
    {
        int mid=l+r>>1;
        if(check(mid))r=mid-1,ans=mid;//check当前的mid是否合法 
        else l=mid+1;
    }
    printf("%d",ans);
}

int main() 
{
    n=read();m=read();//n人 m菜单 
    for(int i=1;i<=n;i++) 
    {
    a[i]=read();a[i+n]=a[i];
    }
    for(int  i=1;i<=2*n;i++)
    sum[i]=sum[i-1]+a[i]; //前缀和 
    dx(1,inf);//inf=1e9+7 
    return  0 ;
}

好了,现在非常感谢你耐心读完了这篇博客。接下来,是ssw02的怀念时间。

上一届给ssw02留下深刻影响的学长:YL , WRY , JZ , LJX , FYT , 还有几位实在记不起名字了,抱歉。
谨纪念 ssw02 于2019.7.13

题解——慕斯蛋糕

标签:set   搜索引擎   前缀   ons   切分   卡住   class   block   顺序   

原文地址:https://www.cnblogs.com/ssw02/p/11181683.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!