洛谷P2471 [SCOI2007] 降雨量

题目链接

题意:给定n个数据,每个数据包括一个年份和当年的降雨量,然后进行m次询问,每次询问给出两个年份Y,X,询问的内容是“X年是自Y年以来降雨量最多的”,你需要回答这句话是“必真”、“必假”还是“有可能”。“X年是自Y年以来降雨量最多的”的定义是X年的降雨量不超过Y年,且对于任意Y < Z < X,Z年的降雨量严格小于X年。

题解

这个题最大的难点在于如何分类讨论,而分类讨论的原因在于某些年份的信息不知道,并且不同位置的情况还不同。

本题还有不少坑点,比如询问的两个年份并不保证Y <= X,也就是说可能会出现Y > X的情况,这样肯定是“必假”的;还有就是分类讨论时不同情况选择的区间也不同。

在处理信息时,我是用了一个pos数组将年份存下来,由于所给的年份是按顺序给出的,所以询问时可以利用二分查找找到第一个不小于询问年份的年份下标,便于之后讨论。还需要用到区间最大值,这个用线段树维护一下即可。

既然不同位置年份信息缺失造成的结果不同,那么我们可以从这个角度入手进行讨论,我主要分了五种情况。以下内容中ul表示第一个不小于Y的年份下标,ur表示第一个不小于X的年份下标,数组a[i]代表年份下标为i的降雨量。

(1)Y > X,这种情况直接输出“false”。
(2)左端信息和右端信息都不确定,这种情况直接输出“maybe”,因为单单这两年的降雨量大小关系就未知,所以肯定不确定。
(3)左端信息确定,右端信息不确定,这个条件下只有两种结果,但要注意ul+1=ur的情况要单独拿出来,因为后面需要查询[ul + 1, ur - 1]区间的最大值,这种情况会导致左边界大于右边界,这种情况直接输出“maybe”,因为只有两个年份并且其中一个降雨量不确定。然后就是查询[ul + 1, ur - 1]区间的最大值,记为mx,如果mx大于等于a[ul],那mx肯定也大于等于a[ur],输出“false”;否则,输出“maybe”。
(4)左端信息不确定,右端信息确定,这种情况和(3)类似,但是要注意单独讨论的情况是ul=ur,然后查询的是[ul, ur - 1]区间的最大值(这里结合画图理解一下即可),其余的和(3)一样。
(5)左右端点信息都确定,首先判断两个端点的关系,如果a[ul] < a[ur],那么不满足条件,直接输出“false”;然后查询[ul + 1, ur - 1]的最大值,记为mx,如果mx大于等于a[ur],那么输出“false”;最后再看中间是否有缺失的年份,这里直接判断ur - ul是否等于X - Y即可,若相等,说明中间没有缺失年份,输出“true”,否则,输出“maybe”。

代码实现

#include <algorithm>
#include <iostream>
#include <stdio.h>
#include <iomanip>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <set>
#include <map>
#define ll long long
using namespace std;
const int N = 5e4 + 50;
int n, m;
int tree[N << 2], a[N], pos[N];
void build(int u, int l, int r){
    if(l == r){
        tree[u] = a[l];
        return;
    }
    int mid = (l + r) >> 1;
    build(u << 1, l, mid);
    build(u << 1 | 1, mid + 1, r);
    tree[u] = max(tree[u << 1], tree[u << 1 | 1]);
}
int query(int u, int l, int r, int ul, int ur){
    if(ul <= l && ur >= r){
        return tree[u];
    }
    int mid = (l + r) >> 1;
    int ans = 0;
    if(ul <= mid) ans = max(ans, query(u << 1, l, mid, ul, ur));
    if(ur > mid) ans = max(ans, query(u << 1 | 1, mid + 1, r, ul , ur));
    return ans;
}
int main(){
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> pos[i] >> a[i];
    build(1, 1, n);
    cin >> m;
    while(m--){
        int l, r;
        cin >> l >> r;
        if(l > r){
            cout << "false" << "\n";
            continue;
        }
        int ul = lower_bound(pos + 1, pos + 1 + n, l) - pos;
        int ur = lower_bound(pos + 1, pos + 1 + n, r) - pos;
        int f1 = (pos[ul] == l), f2 = (pos[ur] == r);
        if(!f1 && !f2){
            cout << "maybe" << "\n";
            continue;
        }else if(f1 && !f2){
            if(ul + 1 == ur){
                cout << "maybe" << "\n";
                continue;
            }
            int mx = query(1, 1, n, ul + 1, ur - 1);
            if(mx >= a[ul]) cout << "false" << "\n";
            else cout << "maybe" << "\n";
        }else if(!f1 && f2){
            if(ul == ur){
                cout << "maybe" << "\n";
                continue;
            }
            int mx = query(1, 1, n, ul, ur - 1);
            if(mx >= a[ur]) cout << "false" << "\n";
            else cout << "maybe" << "\n";
        }else{
            if(a[ul] < a[ur]){
                cout << "false" << "\n";
                continue;
            }
            int mx = query(1, 1, n, ul + 1, ur - 1);
            if(mx >= a[ur]){
                cout << "false" << "\n";
                continue;
            }
            if(r - l == ur - ul) cout << "true" << "\n";
            else cout << "maybe" << "\n";
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值