题目简述
$n$ 排电脑,每排坏了 $a_i$ 台,$m$ 个人修电脑。一开始所有人在第 0 排(从第 1 排开始才有电脑),每一时刻每个人可以前往下一排,或者修自己所在一排的一台坏电脑。问修完电脑的最少时间。$n,m≤10^5,\ a_i≤10^9$
思路
考虑怎么安排修电脑最高效:一个人尽可能只修一排的电脑,除非他修完这排电脑之后还有剩余的时间。可以想到一个人最大化地利用时间就需要走的更少,所以如果多份工作每个人完成一点的话所有人都要奔波于各排电脑之间,有很多人多走了。
基于上面的分配方法,对于给定的时间,将每个人的时间单独考虑,让所有人一个一个完成自己的工作。计算最终能否修完全部的电脑。只需要二分这个时间然后用这种方法判定就可以了。
Code
可以从第一排直接开始修,也可以从每次没有修过最后一排开始吧 $a_i=0$ 的去掉以后从后面开始修。还要注意二分时$r$ 的初始值要把到达下一排的时间加上。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<climits>
#include<queue>
#include<utility>
#include<vector>
#define int long long
using namespace std;
const int MaxN = 100005;
int n, m, a[MaxN], tmp[MaxN];
inline int Read(){
int num = 0, op = 1;
char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') op = -1;
ch = getchar();
}
while(isdigit(ch)){
num = num * 10 + ch - '0';
ch = getchar();
}
return num * op;
}
inline bool Judge(int t){
memcpy(tmp, a, sizeof(a));
int line = n;
for(int i=1; i<=m; i++){
while(line && !tmp[line]) line--;
if(!line) return true;
else if(line > t) return false;
int todo = t - line;
while(line && todo){
int need = min(todo, tmp[line]);
while(line && !tmp[line]) line--;
todo -= need;
tmp[line] -= need;
}
}
while(line && !tmp[line]) line--;
if(!line) return true;
else return false;
}
signed main(){
n = Read(), m = Read();
for(int i=1; i<=n; i++)
a[i] = Read();
int l = 0, r = 1e14+1e10;
while(l<=r){
int mid = (l+r) >> 1;
if(Judge(mid)) r = mid-1;
else l = mid+1;
}
printf("%lld", l);
return 0;
}