这个题的单调性在哪里呢?
显然(额),如果你在分配第个订单时就gg了,那么往后的订单就都没法分配了。
所以,我们二分一下到底是哪个订单时gg了:
假设我们check 分配到第个订单,设数组表示分配完第个订单后第天还有多少个教室供后来的第后的订单分配,那么,当数组中出现的情况时,表示第天的教室已经被多个订单分配且无法满足所有的前个订单。那二分流程就出来了:
1.选取.
2.检查分配到第个订单时是否gg,若gg了,则有可能在分配第个订单前的订单时就已经gg了,令.若没有gg,则令。
最后注意:若出现的情况时,check函数直接返回合法即可。
最后说一说如何实现check函数的判断:
由于我只需要知道分配完第后的数组的值,但修改却是次的。
多次修改,单次查询:差分数组!!!
上代码:
#include<cstdio> #include<cstring> #include<iostream> #define ri register int using namespace std; const int MAXN=1000020; int n,q,a[MAXN],d[MAXN],s[MAXN],t[MAXN],l,r,mid,cha[MAXN],sum[MAXN]; bool check(int lst) { memset(cha,0,sizeof(cha)); memset(sum,0,sizeof(sum)); if(lst==0||lst==n+1) return 1; for(ri i=1;i<=lst;i++) cha[s[i]]-=d[i],cha[t[i]+1]+=d[i];//O(1)修改 //sum[i]:分配完mid个订单后第i天还剩多少个空余教室 //cha[i]:sum[i]-sum[i-1] for(ri i=1;i<=n;i++) sum[i]=sum[i-1]+cha[i]; for(ri i=1;i<=n;i++) { sum[i]+=a[i]; if(sum[i]<0) return 0; } return 1; } inline int read() { int x=0; char ch=getchar(); while(ch<'0'||'9'<ch) ch=getchar(); while('0'<=ch&&ch<='9') { x=(x <<3)+(x <<1)+(ch-'0'); ch=getchar(); } return x; } int main() { n=read(),q=read(); for(ri i=1;i<=n;i++) a[i]=read(); for(ri i=1;i<=q;i++) d[i]=read(),s[i]=read(),t[i]=read(); l=0,r=q+1; while(l+1<r) { mid=(l+r)>>1; if(check(mid)) l=mid; else r=mid; } for(ri i=l;i<=r;i++) if(!check(i)) { cout<<"-1"<<'\n'<<i; return 0; } //注意第l和r个订单都有可能gg,所以按分配顺序从小往大判断 cout<<"0"; return 0; }