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
%d",i);
            return 0;
        }
    }
    printf("0");
    return 0;
}
原文地址:https://www.cnblogs.com/popo-black-cat/p/10354937.html