题面
有(n)个位置((1sim n)),第(i)个位置上有(a_i)个箱子。有(m)个人,开始在(0)位置(即在(1)号位置左边),每一秒钟每个人都可以选择搬走自己位置上的一个箱子或向前走一步(即从位置(i)走到位置
(i+1))。问最少需要多少时间才可以将箱子全部搬完。
输入第一行两个正整数(n, m(n, mle 10^5)),第二行(n)个整数(a_i(0le a_ile 10^9))。
输出一行表示答案。
输入输出样例
输入 #1复制
2 1
1 1
输出 #1复制
4
输入 #2复制
3 2
1 0 2
输出 #2复制
5
输入 #3复制
4 100
3 4 5 4
输出 #3复制
5
思路
模拟加二分
这道题我们的想求出答案来并不好做,但是我们可以带入答案进行验证,也就是我们的二分
每次(check)函数的时候,我们进行模拟,每个人能清除就清除,并记录每个人清到了哪个位置(pos)
当我们的(pos)到了(n+1)的时候,就代表我们肯定到了终点
注意,我(wa)点就是因为退出的边界搞错了,是(n+1),不是(n)
因为可能存在这样一种情况,我们的倒数第二个清理到了(pos)的位置,现在的倒数第一个人该清理第(pos)个
位置,但是最后一个人的时间仍然不够清理完(pos)这个位置,所以我们就只能返回(false)了
代码(还是贴一下吧,容易写错)
#include<bits/stdc++.h>
using namespace std;
const int N=210000;
int b[N],a[N];
int n,m;
inline int read(){
int x = 0;char c = getchar();
while(!isdigit(c)) c = getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x;
}
bool check(long long x)
{
long long pos=1;//下标从1开始,所以我们的位置从1开始
for(register int i=1;i<=n;++i)
{
b[i]=a[i];
}
for(register int i=1;i<=m;++i)
{
long long tim=x-pos;//我们每个人的剩余的时间
while(tim>0)
{
if(pos==n+1) return true;
while(b[pos]==0&&tim>0&&pos<=n)
{
pos++;
tim--;
}
if(tim>0)
{
if(b[pos]>=tim)
{
b[pos]-=tim;
tim=0;
}
else
{
tim-=b[pos];
b[pos]=0;
}
}
}
}
for(register int i=1;i<=n;++i)
if(b[i]>0)
return false;
return true;
}
long long ans;
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1;i<=n;i++)
{
scanf("%d",&a[i]);
}
long long l=0;
long long r=10000000000000000;
while(l<r)
{
long long mid=(l+r)/2;
if(check(mid))
{
ans=mid;
r=mid;//二分的习惯写法
}
else l=mid+1;
}
printf("%lld
",ans);
return 0;
}