BZOJ 1067: [SCOI2007]降雨量(线段树/RMQ)

Description

我们常常会说这样的话:“X年是自Y年以来降雨量最多的”。它的含义是X年的降雨量不超过Y年,且对于任意
Y<Z<X,Z年的降雨量严格小于X年。例如2002,2003,2004和2005年的降雨量分别为4920,5901,2832和3890,
则可以说“2005年是自2003年以来最多的”,但不能说“2005年是自2002年以来最多的”由于有些年份的降雨量未
知,有的说法是可能正确也可以不正确的。

Input

输入仅一行包含一个正整数n,为已知的数据。以下n行每行两个整数yi和ri,为年份和降雨量,按照年份从小
到大排列,即yi<yi+1。下一行包含一个正整数m,为询问的次数。以下m行每行包含两个数Y和X,即询问“X年是
自Y年以来降雨量最多的。”这句话是必真、必假还是“有可能”。

Output

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


题解:
题目已经说的很清楚要求区间最值了…就是判断条件时太恶心了…

解释写在代码里了


AC代码(线段树):

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#include<ext/rope>
using namespace std;
using namespace __gnu_cxx;
#define pii pair<int,int>
#define mp(a,b) make_pair(a,b)
const int MAXN = 5e4+10;
const int MOD = 100003;
const int INF = 0x3f3f3f3f;
int n,Q,a[MAXN],y[MAXN];
struct node{ int l,r,val,mx; }t[MAXN<<2];
inline void pushup(int rt){
    t[rt].mx = max(t[rt<<1].mx,t[rt<<1|1].mx);
}
void build(int rt,int l,int r){
    t[rt].l=l,t[rt].r=r;
    if(l==r){ t[rt].val=t[rt].mx=a[l]; return; }
    int mid=(l+r)>>1;
    build(rt<<1,l,mid); build(rt<<1|1,mid+1,r);
    pushup(rt);
}
int query(int rt,int l,int r){
    if(l<=t[rt].l && t[rt].r<=r) return t[rt].mx;
    int mid = (t[rt].l+t[rt].r)>>1;
    if(r<=mid) return query(rt<<1,l,r);
    else if(l>mid) return query(rt<<1|1,l,r);
    else return max(query(rt<<1,l,mid),query(rt<<1|1,mid+1,r));
}
signed main(){
#ifndef ONLINE_JUDGE
    freopen("C:\\Users\\Administrator\\Desktop\\in.txt","r",stdin);
#endif // ONLINE_JUDGE
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d%d",&y[i],&a[i]);
    build(1,1,n);
    scanf("%d",&Q);
    while(Q--){
        int yy,xx; scanf("%d%d",&yy,&xx);
        if(xx<=yy){ puts("false");continue; }
        int l = lower_bound(y+1,y+n+1,yy)-y;
        int r = lower_bound(y+1,y+n+1,xx)-y;
        bool fl = y[l]==yy,fr = y[r]==xx;//判断输入的两个年份是否为已知数值
        int ans = 0;
        if(l+(fl ? 1:0)<=r-1) ans=query(1,l+(fl ? 1:0),r-1);//[L+1,R-1]中间的最值
        if((fr && ans>=a[r]) || (fl && ans>=a[l]) || (fl && fr && (a[l]<a[r] || ans>=a[r])))
            puts("false");//输入年份已知降雨量才能判断
        //要满足x年的降雨量不超过y年,且中间年份的降雨量严格小于x年
        //x年的降雨量为a[r],y年则为a[l]
        else if(r-l != y[r]-y[l] || !fl || !fr) puts("maybe");
        //中间有不确定的年份,在没有false的情况下才能是maybe
        else puts("true");
    }
    return 0;
}

AC代码(RMQ):

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#include<ext/rope>
using namespace std;
using namespace __gnu_cxx;
#define pii pair<int,int>
#define mp(a,b) make_pair(a,b)
const int MAXN = 5e4+10;
const int MOD = 100003;
const int INF = 0x3f3f3f3f;
int n,Q,a[MAXN],y[MAXN],dp[MAXN][20],lg[MAXN];
inline void RMQ(){
    for(int i=1;i<=n;i++) dp[i][0]=a[i];
    for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
    for(int j=1;(1<<j)<=n;j++)
        for(int i=1;i+(1<<j)-1<=n;i++)
            dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
inline int query(int l,int r){
    int k = lg[r-l+1];
    return max(dp[l][k],dp[r-(1<<k)+1][k]);
}
signed main(){
#ifndef ONLINE_JUDGE
    freopen("C:\\Users\\Administrator\\Desktop\\in.txt","r",stdin);
#endif // ONLINE_JUDGE
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d%d",&y[i],&a[i]);
    RMQ();
    scanf("%d",&Q);
    while(Q--){
        int yy,xx; scanf("%d%d",&yy,&xx);
        if(xx<=yy){ puts("false");continue; }
        int l = lower_bound(y+1,y+n+1,yy)-y;
        int r = lower_bound(y+1,y+n+1,xx)-y;
        bool fl = y[l]==yy,fr = y[r]==xx;
        int ans = 0;
        if(l+(fl ? 1:0)<=r-1) ans=query(l+(fl ? 1:0),r-1);
        if((fr && ans>=a[r]) || (fl && ans>=a[l]) || (fl && fr && (a[l]<a[r] || ans>=a[r])))
            puts("false");
        else if(r-l != y[r]-y[l] || !fl || !fr) puts("maybe");
        else puts("true");
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值