原题: http://codeforces.com/problemset/problem/1132/D
题意: k-1分钟,n个点,初值为ai,每分钟消耗bi。你现在可以选择一个p大小的充电器,每分钟选择一个点充。问最小的p使得没有一个点在任意一分钟小于0。
解析:
二分答案值,对于答案p进行分析。
对于每个点,只有当它变成负数的时候再+p。而如果前面有几分钟没有加过,我可以让那分钟充这个点。形象一定,如果得出的增加时间为1、3、4、4、5,那么我可以让空出来的2来充4,所以只要满足 即可。
这个在什么时候判呢?因为充电次数最多为k-1,所以我可以每次充电时候查询当前时间即可。
最后再从1~k判一遍即可。
为什么最后还要再判一遍?例如第一个点用了2、2、3,发现没什么问题,第二点用了1,也没有问题。但是结果是错的。
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(int)(a);i<=(int)(b);i++)
#define LL long long
const int maxn=2e5+5;
LL a[maxn],b[maxn];
LL tr[maxn];
int n,k;
void update(int p,int v){
while(p<=k){
tr[p]+=v;
p+=p&-p;
}
}
LL query(int p){
LL res=0;
while(p){
res+=tr[p];
p-=p&-p;
}
return res;
}
bool check(LL p){
memset(tr,0,sizeof tr);
rep(i,1,n){
LL ct=1;
LL now=a[i];
while(1){
LL sub=now/b[i];
ct+=sub;
now-=sub*b[i];
if(ct>=k)break;
now-=b[i];
while(now<0){
update(ct,1);
if(query(ct)>ct)return 0;
now+=p;
}
ct++;
}
}
rep(i,1,k){
if(query(i)>i)return 0;
}
return 1;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>k;
rep(i,1,n)cin>>a[i];
rep(i,1,n)cin>>b[i];
if(k==1){
cout<<0<<endl;return 0;
}
LL l=0,r=1e18;
if(check(0))cout<<0<<endl;
else if(!check(1e18))cout<<-1<<endl;
else{
while(r-l>1){
LL mid=l+r>>1;
if(check(mid))r=mid;
else l=mid;
}
cout<<r<<endl;
}
}