P1083 借教室
正解是二分……
emmm……
我能说什么……
鬼知道是二分啊!
一开始想到的是线段树qwq
于是乎——我就用线段树做的!
我已开始一直在被一个问题困扰着,就是lazy标记的下传,觉得有点小麻烦,但是还是想出了下传的方法。可写着写着,突然发现似乎并不用下传,于是我就试了一下,竟然AC了!!!
事后我才知道,这就是标记永久化的线段树……
当前点的值=min(左儿子min-左儿子标记,右儿子min-右儿子标记)。
当然最后查询根节点的min时也要减去根节点的标记。
标记永久化的好处是速度会变快,以及便于删除标记。但是这道题没有删除操作。
但是它也有它的局限性,它只支持每一次整体的询问,如果你要询问其中的某一段区间,标记永久化线段树可就要出锅啦……
接下来上代码:
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> using namespace std; #define maxn 4*1000005 struct node { int minn,lazy; } e[maxn]; int l[maxn],r[maxn]; void build(int L,int R,int now) { l[now]=L; r[now]=R; if(L==R) { scanf("%d",&e[now].minn); return ; } int mid=(L+R)>>1; build(L,mid,now<<1); build(mid+1,R,now<<1|1); e[now].minn=min(e[now<<1].minn,e[now<<1|1].minn); } void forever(int now) { int ls=now<<1; int rs=now<<1|1; e[now].minn=min(e[ls].minn-e[ls].lazy,e[rs].minn-e[rs].lazy); return ; } void update(int L,int R,int k,int now) { if(L==l[now]&&R==r[now]) { e[now].lazy+=k; return ; } int mid=(l[now]+r[now])>>1; if(R<=mid) update(L,R,k,now<<1); else if(L>mid) update(L,R,k,now<<1|1); else { update(L,mid,k,now<<1); update(mid+1,R,k,now<<1|1); } forever(now); } int main() { int n,m; scanf("%d%d",&n,&m); build(1,n,1); for(int i=1;i<=m;i++) { int l,r,k; scanf("%d%d%d",&k,&l,&r); update(l,r,k,1); if(e[1].minn-e[1].lazy<0) { printf("-1\n%d",i); return 0; } } printf("0"); return 0; }