P9519题解
看到这道题第一反应就是二分,二分最终到底发多少工资。
那么现在问题就变成了,如何在线性时间内判断一个给定的
我们意识到,朴素的
这启发我们思考,能否通过重复减一的方式来获得某个位置对当前位置的贡献呢?
答案当然是肯定的。
我们定义一个队列
这样我们就获得了
然后再逆序做一遍,就可以获得
这样我们就在线性复杂度内完成了对某个
代码:
#include<bits/stdc++.h>
using namespace std;
int n,a[1000005],ans,m,b,l=1,r,mid;
bool s[1000005];
long long c[1000005];
inline bool check(int k){
queue<int> q;
long long sum=0;
memset(c,0,sizeof(c));
for(int i=1;i<=n;i++){
sum-=q.size();
if(!q.empty()&&i-q.front()>=k)q.pop();
if(s[i])sum+=k,q.push(i);
c[i]+=sum;
}
while(!q.empty())q.pop();sum=0;
for(int i=n;i;i--){
sum-=q.size();
if(!q.empty()&&q.front()-i>=k)q.pop();
if(s[i])sum+=k,q.push(i);
c[i]+=sum;
if(s[i])c[i]-=k;//注意特判,如果 i 本身发了工资,那么会被统计两次,需要减去重复的。
}
for(int i=1;i<=n;i++)if(c[i]<a[i])return 0;
return 1;
}
int main(){
ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i],r=max(r,a[i]);
for(int i=1;i<=m;i++)cin>>b,s[b]=1;
r+=n;//需求最高的人不一定发工资,所以加上 n 之后才是真正的二分上限。
while(l<=r){
mid=(l+r)>>1;
if(check(mid))ans=mid,r=mid-1;
else l=mid+1;
}
cout<<ans;
}