二分k+差分检查
我们可以把每天能借的教室数量用数组存下来。当第i天到第j天需要借出k间教室时,a[i]到a[j]的数都减k。这样就能用差分数组来完成。
若前k份订单可以,则前面k-1份都可以;若第k份不行,则后面都不行,因此想到二分
每次都对前k份订单用差分数组维护,看看是否会减成负数
这里用的差分数组并不是严格意义上的差分,而可以理解成是相对于原数组的差分。
例如,原数组a为1 2 8 3 5 (下标从1开始),现在需要把下标2到4的数都减1
这时候,差分数组d可以表示为0 -1 0 0 1
那么如何求之后的数组a呢?
以a中下标0的元素为参照(假设它存在且保持不变),由于d[1]=0,因此a[1]相对于a[0]不变,a[1]'=1。
d[2]=-1,表明a[2]相对a[1]减小了1,相对a[0]也减小了1,a[2]'=1
d[3]=0,表明a[3]相对a[2]不变,由于a[2]相对a[0]减小1,因此a[3]相对a[0]减小1
即a[3]'=7
同理a[4]'=2
d[5]=1,表明a[5]相对a[4]增加了1,则相对a[0]不变,即a[5]不变
综上,a[0]也可以理解成是原本的这个数,d[1]+...+d[i],算出来就是a[i]'相对原先的a[i]变化了多少
所以由差分构造r'数组的代码就出来了:
for(int i=1;i<=n;i++){ cha[i]+=cha[i-1]; r1[i]+=cha[i]; }
先求出差分的前缀,再与原来的数相加,即可实现
#include <bits/stdc++.h> using namespace std; typedef long long ll; int r[1000010],d[1000010],s[1000010],t[1000010]; int n,m; bool judge(int mid){ int cha[1000010]={0};//差分数组 int r1[1000010]={0}; for(int i=1;i<=n;i++) r1[i]=r[i];//拷贝数组r for(int i=1;i<=mid;i++){ cha[s[i]]-=d[i]; cha[t[i]+1]+=d[i]; } for(int i=1;i<=n;i++){ cha[i]+=cha[i-1]; r1[i]+=cha[i]; if(r1[i]<0) return false;//教室不够分 } return true; } int main(){ ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); cin >> n >> m; for(int i=1;i<=n;i++){ cin >> r[i]; } for(int i=1;i<=m;i++){ cin >> d[i] >> s[i] >> t[i]; } int l=0,r=m; while(l<=r){ int mid=(l+r)>>1; if(judge(mid)) l=mid+1; else r=mid-1; } if(l-1==m) cout << "0\n"; else{ cout << "-1\n"; cout << l << "\n"; } return 0; }