题解——慕斯蛋糕

慕斯蛋糕

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

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

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

题面

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

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

题目思路:

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

posted @ 2019-07-13 19:30  蓝银杏-SSW  阅读(234)  评论(0编辑  收藏  举报
//结束