洛谷 P1038 [NOIP2012] 借教室

学校大门

简析

很显然这是一道差分+二分答案的题但我偏不这么写线段树的裸题。裸到什么程度呢?你需要实现一颗支持如下操作的线段树:

  • 递归建树,自底向上(不然你要执行n次add操作吗?慢死你)

  • 区间修改(这个不用说,把预约了的教室数量减掉)

  • 查询区间最小值(一旦这个最小值小于0,则判断不满足题意)

值得注意的是,由于是查询区间最小值,所以在初始化时应当将线段树节点所存的值设为无穷大,并很显然地将 \(pushup\) 操作改为取左右子树的较小值即可。同时,在每次 \(pushup\) 操作完毕之后,检查一下当前节点值是否小于 \(0\),使用一个全局 \(bool\) 变量 \(flag\) 记录是否满足条件,如果不满足,将 \(flag\) 归零并及时结束所有操作直接按题意输出当前的预约编号即可(对于单组数据节省时间的办法)。

由于线段树常数比较大,实测第十三个测试点会被卡,加个快读就能过了,可以说是非常水蓝的题了。

AC代码

#include<iostream>
#include<cstdio>
#include<cctype>
using namespace std;
const int inf = (1<<31)-1;
int read(){
    int re = 0,ch = getchar();
    while(!isdigit(ch)) ch = getchar();
    while(isdigit(ch)) re = (re<<1) + (re<<3) + ch - '0',ch = getchar();
    return re;
}
struct node{
    int l,r,sum,add;
    #define l(x) t[x].l
    #define r(x) t[x].r
    #define sum(x) t[x].sum
    #define add(x) t[x].add
    #define mid(x) (t[x].l + t[x].r >> 1)
}t[4000006];
bool flag = 1;
int num[1000006];
void check(int x){
    if(sum(x) < 0) flag = 0;
    //cout << "Now node is" << l(x) << ' ' << r(x) << endl << "Val is" << sum(x) << endl << endl; 
}
void pushup(int x){
    sum(x) = min(sum(x<<1),sum(x<<1|1));
}
void pushdown(int x){
    if(add(x)){
        sum(x<<1) -= add(x);
        sum(x<<1|1) -= add(x);
        add(x<<1) += add(x);
        add(x<<1|1) += add(x);
        add(x) = 0;
    }
}
void build(int x,int l,int r){
    l(x) = l;
    r(x) = r;
    add(x) = 0;
    sum(x) = inf;
    if(l == r){
        sum(x) = num[l];
        return;
    }
    build(x<<1,l,mid(x));
    build(x<<1|1,mid(x) + 1,r);
    pushup(x);
    //cout << "Now node is" << l(x) << ' ' << r(x) << endl << "Val is" << sum(x) << endl << endl; 
} 
void addnum(int x,int l,int r,int v){
    if(!flag) return;
    if(l <= l(x) && r >= r(x)){
        add(x) += v;
        sum(x) -= v;
        return;
    }
    pushdown(x);
    if(l <= mid(x)) addnum(x<<1,l,r,v);
    if(r > mid(x)) addnum(x<<1|1,l,r,v);
    pushup(x);
    check(x);
}
int n,m;
int main(){
    n = read(),m = read();
    for(int i = 1;i <= n;i++) num[i] = read();
    build(1,1,n);
    for(int i = 1;i <= m;i++){
        int d = read(),s = read(),t = read();
        flag = 1;
        addnum(1,s,t,d);
        if(!flag){
            printf("-1\n%d",i);
            return 0; 
        }
    }
    printf("0\n");
    return 0;
}

转载于:https://www.cnblogs.com/mysterious-garden/p/9834009.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值