最显然的方法就是线段树
稍微卡一下常就过了
还有另一种方法,差分
但是有一个问题,我们怎么知道是谁使订单无法满足呢
一种直接的想法就是二分
但是复杂度为 O(nlogm) 跟线段树O(mlogn)一样,但是常数小可过此题
重点是讲 O(n+m) 的方法
同样差分,从左到右一个个扫过去
如果出现了负数就把订单往回撤,一直撤到变成正数,然后继续扫
扫完后每天都符合要求了,并且如果再多一份订单就会出现负数
所以最后答案就是撤到的订单编号加1
撤回过程中的具体操作还是看代码吧
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<map> using namespace std; typedef long long ll; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=1e6+7; int l[N],r[N],d[N],a[N]; int n,m; ll now,tag[N]; int main() { n=read(); m=read(); for(int i=1;i<=n;i++) a[i]=read(); for(int i=1;i<=m;i++) { d[i]=read(); l[i]=read(); r[i]=read(); tag[l[i]]+=d[i]; tag[r[i]+1]-=d[i]; } int pos=m;//记录当前撤回到哪个订单 for(int i=1;i<=n;i++) { now+=tag[i]; while(now>a[i]&&pos)//如果需求超过供给就要往回撤,注意保证pos>0,不然会RE { tag[l[pos]]-=d[pos]; tag[r[pos]+1]+=d[pos];//撤回订单 if(l[pos]<=i&&r[pos]>=i) now-=d[pos];//如果有包含当前位置,now也要更新 pos--; } } if(pos==m) { printf("0"); return 0; } printf("-1 %d",pos+1); }