题目大意:
有一个n个数的数列,m个操作,第i个操作使[li,ri]区间建di,问第几个操作使数列中出现负数。
思路:
暴力显然过不了,那么就可以优化了,不难想到线段树,显然需要良好的姿势,那么就差分。
a[i]表示第i天比第i-1天多了多少房间,于是a的前缀和即为该天的房间数量。而a的维护显然为a[li]+=di,a[ri+1]-=di。
因为求最前的操作,于是我们可以二分答案。但如此常数比较大,又有冗余,可以来个栈一样的东西节省时间。
但是有大神想到了O(n+m)的算法。假设m个指令都可满足,天数从前往后判断,再从后往前一个一个删除指令,直到无负数为止。
代码:
二分:
1 #include<cstdio> 2 const int M=1000009; 3 int n,m,i,k,h,t,sum,last,a[M],l[M],r[M],d[M],b[M]; 4 bool can; 5 6 int read() 7 { 8 int x=0; 9 char ch=getchar(); 10 while (ch<'0' || ch>'9') ch=getchar(); 11 while (ch>='0' && ch<='9') x=(x<<1)+(x<<3)+ch-48,ch=getchar(); 12 return x; 13 } 14 15 int main() 16 { 17 n=read(),m=read(); 18 for (i=1;i<=n;++i) b[i]=read(); 19 for (i=1;i<=m;++i) d[i]=read(),l[i]=read(),r[i]=read(); 20 for (h=1,t=m;h<t;) 21 { 22 m=h+t>>1; 23 if (m>last) for (i=last+1;i<=m;++i) a[l[i]]=a[l[i]]+d[i],a[r[i]+1]=a[r[i]+1]-d[i]; 24 else if (m<last) for (i=m+1;i<=last;++i) a[l[i]]=a[l[i]]-d[i],a[r[i]+1]=a[r[i]+1]+d[i]; 25 for (last=m,sum=0,i=can=1;i<=n;++i) 26 { 27 sum=sum+a[i]; 28 if (sum>b[i]) { can=0; break; } 29 } 30 if (can) h=m+1; else t=k=m; 31 } 32 if (k) printf("-1\n%d\n",k); 33 else printf("0\n"); 34 return 0; 35 }
O(n+m):
1 #include<cstdio> 2 const int M=1000005; 3 int n,m,i,x,cnt,a[M],b[M],l[M],r[M],d[M]; 4 5 int read() 6 { 7 int x=0; char ch=getchar(); 8 while (ch<'0' || ch>'9') ch=getchar(); 9 while (ch>='0' && ch<='9') x=(x<<1)+(x<<3)+ch-48,ch=getchar(); 10 return x; 11 } 12 13 int main() 14 { 15 n=read(),m=read(); 16 for (i=1;i<=n;++i) a[i]=(b[i]=read())-b[i-1]; 17 for (i=1;i<=m;++i) d[i]=read(),a[l[i]=read()]=a[l[i]]-d[i],a[(r[i]=read())+1]=a[r[i]+1]+d[i]; 18 for (x=m,i=1;i<=n;++i) 19 for (cnt=cnt+a[i];cnt<0;--m) 20 if (l[m]>i) a[l[m]]=a[l[m]]+d[m],a[r[m]+1]=a[r[m]+1]-d[m]; 21 else if (r[m]>=i) cnt=cnt+d[m],a[r[m]+1]=a[r[m]+1]-d[m]; 22 if (m<x) printf("-1\n%d\n",m+1); 23 else printf("0\n"); 24 return 0; 25 }