题目:洛谷P1083、Vijos P1782、codevs1217。
题目大意:有n天,每天可以借a[i]个教室。有m个请求,每个请求要在一段连续天数内借固定数目的教室。请求只能按顺序批准。求第一个无法批准的请求是第几个请求,或全部可以批准。
解题思路:看到区间,容易想到线段树维护(正解貌似是二分,燃鹅我只能理解线段树啊~)。这里显然只需维护最小值。不过如果姿势不好,可是会T的哟。
线段树肯定很好想,但想不超时,就得优化。例如读入优化、标记永久化、递归过程能用全局变量用全局变量、搜到答案直接退出等。最后提交,坐等AC即可。
C++ Code:
#include<cstdio> #include<cstdlib> #include<cctype> using namespace std; long long d[4000005],p[4000005]; int i,n,m,L,R,k; inline long long min(long long a,long long b){return(a<b)?a:b;} inline int readint(){ char c=getchar(); int p=0; for(;!isdigit(c);c=getchar()); for(;isdigit(c);c=getchar())p=(p<<3)+(p<<1)+(c^'0'); return p; } void make_tree(int l,int r,int o){ p[o]=0; if(l==r){ d[o]=readint(); return; } int mid=l+r>>1,lf=o<<1,rg=o<<1|1; make_tree(l,mid,lf); make_tree(mid+1,r,rg); d[o]=min(d[lf],d[rg]); } void change(int l,int r,int o){ if(L<=l&&r<=R){ p[o]+=k; if(d[o]-p[o]<0){ printf("-1 %d ",i); exit(0); } return; } int mid=l+r>>1,lf=o<<1,rg=o<<1|1; if(L<=mid)change(l,mid,lf); if(mid<R)change(mid+1,r,rg); d[o]=min(d[lf]-p[lf],d[rg]-p[rg]); if(d[o]-p[o]<0){ printf("-1 %d ",i); exit(0); } } int main(){ n=readint(),m=readint(); make_tree(1,n,1); for(i=1;i<=m;++i){ k=readint(),L=readint(),R=readint(); change(1,n,1); } puts("0"); return 0; }
--------------------------------------------------------2017-10-11--------------------------------------------------------
偶然想到这道题,突然就想到了二分的方法。
可以发现,当订单无法满足时,小于答案的所有订单都可以满足,而大于等于答案的都不能满足。
利用这个特性,我们就可以进行二分。
重点是如何验证答案呢?
可以发现知道了答案就相当于区间求和问题,差分一遍,然后对于每一天判断借的教室数量有没有超出限制就可以了。
时间复杂度和线段树是一样的,但线段树常数特别大,如果出一个很大的且能满足所有订单的数据,就不能中途退出,就可能被卡掉。
C++ Code:
#include<cstdio> #include<cctype> #include<cstring> using namespace std; #define ll long long ll p[1000005],s[1000005]; int n,m; struct Dd{ int num,b,e; }q[1000005]; template<typename T> inline void read(T& p){ char c=getchar(); for(;!isdigit(c);c=getchar()); for(p=0;isdigit(c);c=getchar())p=(p<<3)+(p<<1)+(c^'0'); } bool ok(int ans){ memset(s,0,sizeof s); for(int i=1;i<=ans;++i) s[q[i].b]+=q[i].num,s[q[i].e+1]-=q[i].num; ll now=0; for(int i=1;i<=n;++i) if((now+=s[i])>p[i])return false; return true; } int main(){ read(n); read(m); for(int i=1;i<=n;++i)read(p[i]); for(int i=1;i<=m;++i) read(q[i].num),read(q[i].b),read(q[i].e); int l=0,r=m,ans=0; while(l<=r){ int mid=(l+r)>>1; if(ok(mid))l=(ans=mid)+1;else r=mid-1; } if(ans==m)puts("0");else printf("-1 %d ",ans+1); return 0; }