题解:
把题面浓缩一下:给定长度为n的正整数序列,m个区间操作,每次操作是把区间[s,t]内所有的数都减去一个d。求使得序列第一次出现负数的最小操作。
由于序列随着操作的进行时不断减小的,也就是说,若第i次操作使得序列出现负值,那么对大于i的操作也必定会使得序列出现负值。所以,我们可以二分答案,二分操作数。设当前二分的操作数是i,我们就用差分执行前i个操作,若出现负值,说明我们可以将二分右边界缩小,若无负值,就可以将二分左边界扩大。这样的时间复杂度是O(nlogn)。
代码:
#include<bits/stdc++.h> using namespace std; const int N=1000000; int n,m,ans; struct point{ int d,s,t; }p[N+5]; int r[N+5],b1[N+5],b2[N+5]; int read(){ int x=0; char ch=getchar(); while(ch<'0' || ch>'9') ch=getchar(); while(ch>='0' && ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();} return x; } bool check(int x){ bool flag=false; memcpy(b2,b1,sizeof(b1)); for(int i=1;i<=x;++i){ b2[p[i].s]-=p[i].d; b2[p[i].t+1]+=p[i].d; } for(int i=1;i<=n;++i){ b2[i]+=b2[i-1]; if(b2[i]<0){ flag=true; break; } } return flag; } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) r[i]=read(); for(int i=1;i<=m;++i) p[i].d=read(),p[i].s=read(),p[i].t=read(); for(int i=1;i<=n;++i) b1[i]=r[i]-r[i-1]; int l=1,r=n; while(l<=r){ int mid=(l+r)>>1; if(check(mid)){ ans=mid; r=mid-1; }else l=mid+1; } if(!ans) printf("0\n"); else printf("-1\n%d",ans); return 0; }