P1083 借教室

传送门

最显然的方法就是线段树

稍微卡一下常就过了

还有另一种方法,差分

但是有一个问题,我们怎么知道是谁使订单无法满足呢

一种直接的想法就是二分

但是复杂度为 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\n%d",pos+1);
}

 

posted @ 2018-10-29 11:24  LLTYYC  阅读(160)  评论(0编辑  收藏  举报