CodeForces 551C
題意有一個教授要走一條路,但是一條路上堆了很多箱子。這個教授有很多學生,問學生把這條路上的箱子全部搬完需要多長時間。
每個學生在每秒都可以在做出兩個選擇,搬掉一個箱子,向前走一步。
比如第二個樣例。3 2
1 0 2;兩個學生都走到第一堆花費1秒。第一個學生搬掉第一堆的一個箱子,第二個學生向前走一步,花費一秒。
第2個學生向前走一步,花費一秒。搬空第3堆花費2秒。一共5秒。
思路:我們二分枚舉時間t。這裏注意一下這個時間t並不能由枚舉得出,而要通過不斷縮小區間得出。WA了一上午,真菜呀。首先得到
時間t之後,我們看這個時間是否滿足條件。最後一個不爲0的堆總要有人搬空,而且這個最浪費時間。那麼我們就先搬這個。那麼這個
人能搬的盒子數x=t-i(第i堆)。如果這個人能搬空這一堆。就讓他搬前一堆。直到利用完這個人。然後接着再利用下一個人。直到搬空
所有堆爲止。如果我們需要的人數大於給的人數。那麼說明枚舉的這個t比較小。不夠用。如果我們需要的人數<=給的人數。那麼這個t
可能是最小的也可能不是小的t。至於爲啥,想一下樣例3就會明白。所以我們無法知道這個t到底是不是我們要找的t!就會出現可能這個
t就是我們要找的t,但是需要的人數小於給的人數。你說我們是要這個t還是不要呢。(我肯定會要,程序就不知道了(-,-))。所以
只能不斷縮小這個區間。最後枚舉兩個l,r端點來判斷了。代碼如下:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <queue>
#include <stack>
using namespace std;
typedef long long ll;
ll n,stu,num[100100],tem[100100],flag,top;
int chik(ll tie)
{
int i;
ll tm,jud=0,peo=0;
for(i=1;i<=n;i++)tem[i]=num[i];
for(i=top;i>=1;i--)//搬空所有箱子,在t時間下,需要多少人。
{
if(tem[i])
{
tm=tie-i;
if(tm<=0) return 0;//無法搬最後一堆,時間小
while(tm>=tem[i]&&i>=1)
{
tm-=tem[i];
tem[i]=0;
i--;
}
peo++;
if(peo>stu) return 0;//人多了,時間小
if(tm>0) {tem[i]-=tm;tm=0;}
i++;
}
}
return peo<=stu;//可能時間多,也可能正好
}
ll ef(ll l,ll r)
{
ll mid;
ll ans;
while(l+1<r)
{
mid=(l+r)/2;
ans=chik(mid);
if(ans) r=mid;//不能寫成mid+1,因爲你不知道mid是不是答案
else l=mid;//不能寫成mid-1,同理
}
if(chik(r)) printf("%lld\n",r);
else printf("%lld\n",l);
}
int main()
{
ll sum=0;
scanf("%lld%lld",&n,&stu);
for(int i=1;i<=n;i++)
{scanf("%lld",&num[i]);sum+=num[i];}
sum+=n;top=n;
while(num[top]<=0) top--;
ef(0,sum);
return 0;
}