(上一篇博客正好有个跟这个差不多的题:借教室可以练练速度)
大致就是城墙共n块,第i个位置的弓箭手可以保护[i-r,i+r]范围的城墙,现在还可以放k个弓箭手,问这n块城墙中被保护的最少的那块地最大值是多少。
思路:
首先可以限定范围为初始值最小值minn到minn+k+1,然后用查分数组和前缀和维护每段区间的值即可。
#include <bits/stdc++.h>
using namespace std;
const int maxn=5*1e5+100;
long long n,k,r;
long long cha[maxn],sum[maxn];
long long c[maxn],s[maxn];
bool check(long long x){
long long kk=k;
memcpy(c,cha,sizeof cha);
for (int i=1;i<=n;i++){
s[i]=s[i-1]+c[i];
if (s[i]<x) {
if(kk<x-s[i]) return false;
kk-=x-s[i];
if (i+2*r+1<=n) c[i+2*r+1]-=x-s[i];
s[i]=x;
}
}
return true;
}
int main(){
ios::sync_with_stdio(false);
cin>>n>>r>>k;
for (int i=1;i<=n;i++){
long long x;
cin>>x;
if (i-r>0) cha[i-r]+=x;else cha[1]+=x;
if (i+r+1<=n) cha[i+r+1]-=x;
}
long long minn=1e15,maxx=0;
for (int i=1;i<=n;i++) {
sum[i]=sum[i-1]+cha[i];
minn=min(minn,sum[i]);
}
maxx=minn+k+1;
long long ans=0;
while (minn<maxx){
long long mid=(maxx+minn)>>1;
if (check(mid)) ans=max(ans,mid),minn=mid+1;
else maxx=mid;
}
cout<<ans<<endl;
return 0;
}