[Luogu P2471] [SCOI2007]降雨量

本文解析了一道关于历史降雨量比较的算法题,通过线段树实现高效查询与更新,讨论了特判条件和注意事项,如边界处理、查询值匹配及严格不等式判断。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

洛谷传送门

题目描述

我们常常会说这样的话:“X年是自Y年以来降雨量最多的”。它的含义是X年的降雨量不超过Y年,且对于任意Y

输入输出格式

输入格式:

输入仅一行包含一个正整数n,为已知的数据。以下n行每行两个整数yi和ri,为年份和降雨量,按照年份从小到大排列,即yi

输出格式:

对于每一个询问,输出true,false或者maybe。

输入输出样例

输入样例#1:

6
2002 4920
2003 5901
2004 2832
2005 3890
2007 5609
2008 3024
5
2002 2005
2003 2005
2002 2007
2003 2007
2005 2008

输出样例#1:

false
true
false
maybe
false

说明

100%的数据满足:
1<=n<=50000,1<=m<=10000,109<=yi<=109,1<=ri<=109 1 <= n <= 50000 , 1 <= m <= 10000 , − 10 9 <= y i <= 10 9 , 1 <= r i <= 10 9

解题分析

博主在4个月前还是一枚萌新的时候打过这道题, 被各种特判坑到爆炸, 最后怎么调都有5个RE…

4个月后码力大增的博主又来尝试了这道题, 发现时间复杂度并不是问题, 于是为了避免线段树查询时出现左右区间爆炸的情况, 进行了暴力修改操作再改回去…
另外要注意一下坑点:

1.线段树查询的时候可能左边界大于右边界!因为左右边界可能长度只有1或2, 将左边界右边界++–后可能就爆炸了…(因此博主选择将左边界右边界修改为0)

2.lower_bound得到的位置所对应的值可能大于或小于查询值! 特别注意小于的情况, 因为可能查询的左区间比已知的最大年份都大…

3.注意题目中严格大于和大于等于的关系… 因此在判定是false 或 maybe 的时候是要判定区间中部的值是否大于等于左边界或右边界的值…

剩下的就只有码代码啦(10分钟)!调试两小时

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cctype>
#include <cstdlib>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define ls tree[now].son[0]
#define rs tree[now].son[1]
#define MX 100005
#define lb tree[now].lef
#define rb tree[now].rig
static bool fu;
template <class T>
IN void in(T &x)
{
    fu = false;
    x = 0; R char c = gc;
    W (!isdigit(c)) 
    {if(c == '-' ) fu = true; c = gc;}
    W (isdigit(c))
    {x = (x << 1) + (x << 3) + c - 48, c = gc;}
    if(fu) x = -x;
}
namespace SGT
{
    using std::max;
    struct Node
    {
        int val, son[2], mx, lef, rig;
    }tree[MX << 2];
    struct Data
    {
        int year, val;
    }dat[MX];
    IN bool operator < (const Data &x, const Data &y)
    {return x.year < y.year;}
    int dot, q;
    IN void pushup(const int &now)
    {tree[now].mx = max(tree[ls].mx, tree[rs].mx);}
    void build(const int &now, const int &lef, const int &rig)
    {
        lb = lef, rb = rig;
        if(lb == rb)
        {
            tree[now].val = tree[now].mx = dat[lb].val;
            return;
        }
        int mid = (lef + rig) >> 1;
        ls = now << 1; rs = now << 1 | 1;
        build(ls, lb, mid);
        build(rs, mid + 1, rb);
        pushup(now);
    }
    int query(const int &now, const int &lef, const int &rig)
    {
        if(lb >= lef && rb <= rig) return tree[now].mx;
        int mid = (lb + rb) >> 1;
        int rt = -1;
        if(mid >= lef) rt = max(rt, query(ls, lef, rig));
        if(mid < rig) rt = max(rt, query(rs, lef, rig));
        return rt;
    }
    void modify(const int &now, const int &tar, const int &del)
    {
        if(lb == tar && rb == tar)
        {
            tree[now].val = del;
            tree[now].mx = del;
            return;
        }
        int mid = (lb + rb) >> 1;
        if(tar <= mid) modify(ls, tar, del);
        else modify(rs, tar, del);
        pushup(now);
    }
    IN short work(const int &a, const int &b)
    {
        if(a > b) return 2;
        if(a == b) return 1;
        bool emp = false, big = false;//emp 指中间有未知年份的情况
        int lef = std::lower_bound(dat + 1, dat + 1 + dot, (Data){a, 0}) - dat;
        if(dat[lef].year != a) emp = true;//注意不能只判小于
        if(dat[lef].year < a) lef++;
        int rig = std::lower_bound(dat + 1, dat + 1 + dot, (Data){b, 0}) - dat;
        if(dat[rig].year != b) emp = true;//同上
        if(dat[rig].year > b) rig--;
        if(rig <= lef) return 3;//非法
        if(dat[rig].year != b && dat[lef].year != a) return 3;//两边都不知道, 直接返回maybe
        if(rig - lef != b - a) emp = true;//中间有空位(数量不对应)
        if(dat[rig].year == b && dat[lef].year == a)
        {
            int va1 = query(1, lef, lef);
            int va2 = query(1, rig, rig);
            if(va1 < va2) return 2;
            modify(1, lef, 0);
            modify(1, rig, 0);
            if(query(1, lef, rig) >= va2) 
            {modify(1, lef, va1), modify(1, rig, va2); return 2;}
            modify(1, lef, va1);
            modify(1, rig, va2);
            if(emp) return 3;
            return 1;
        }
        else if(dat[rig].year == b)
        {
            int va1 = query(1, rig, rig);
            modify(1, rig, 0);
            if(query(1, lef, rig) >= va1) {modify(1, rig, va1); return 2;}
            modify(1, rig, va1);
            return 3;
        }
        else
        {
            int va1 = query(1, lef, lef);
            modify(1, lef, 0);
            if(query(1, lef, rig) >= va1) {modify(1, lef, va1); return 2;}
            modify(1, lef, va1);
            return 3;
        }
    }
}
using namespace SGT;
int main(void)
{
    int a, b;
    in(dot);
    for(R int i = 1; i <= dot; ++i) in(dat[i].year), in(dat[i].val);
    build(1, 1, dot);
    in(q);
    W (q--)
    {
        in(a), in(b);
        switch(work(a, b))
        {
            case 1: printf("true\n"); break;
            case 2: printf("false\n"); break;
            case 3: printf("maybe\n"); break;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值