[BZOJ1067][SCOI2007]降雨量(线段树)

211 篇文章 0 订阅
70 篇文章 0 订阅

题目描述

传送门

题解

离线离散化, 声明的年份和查找的年份一起建线段树。
线段树维护一下区间最大值,两个点之间是否有没有声明的年份打标记。
判断比较吃屎。
我大的分了两种情况:一个是需要在线段树中查询的,一个是直接判断的。
查询了之后,有好几种情况,分别按照是否出现、权值还有中间是否有相隔的年份来判断。

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<map>
using namespace std;

const int max_n=1e5+5;
const int max_tree=max_n*5;
const int INF=1e9;

struct hp{
    int Maxn,Flag;
};
struct hq{
    int year,val;
    bool flag;
}a[max_n];
struct ho{
    int x,y;
}ask[max_n];
int n,m,N,x,y,lrange,rrange;
int year[max_n],val[max_n];
bool flag[max_n];
int maxn[max_tree],delta[max_tree];
map <int,int> hash;
map <int,bool> pd;

inline int in(){
    int x=0,f=1; char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1; 
        ch=getchar();
    }
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}

inline int cmp(hq a,hq b){
    return a.year<b.year||(a.year==b.year&&a.val>b.val)||(a.year==b.year&&a.val==b.val&&a.flag>b.flag);
}

inline void update(int now){
    maxn[now]=max(maxn[now<<1],maxn[now<<1|1]);
    delta[now]=delta[now<<1]&&delta[now<<1|1];
}

inline void build(int now,int l,int r){
    int mid=(l+r)>>1;
    if (l==r){
        maxn[now]=val[l];
        delta[now]=flag[l];
        return;
    }
    build(now<<1,l,mid);
    build(now<<1|1,mid+1,r);
    update(now);
}

inline hp query(int now,int l,int r,int lrange,int rrange){
    hp ans;
    int mid=(l+r)>>1;
    int MAXN=0;
    bool FLAG=true;

    if (lrange<=l&&r<=rrange)
      return ans=(hp){maxn[now],delta[now]};

    if (lrange<=mid){
        ans=query(now<<1,l,mid,lrange,rrange);
        MAXN=max(MAXN,ans.Maxn);
        FLAG=FLAG&&ans.Flag;
    }
    if (mid+1<=rrange){
        ans=query(now<<1|1,mid+1,r,lrange,rrange);
        MAXN=max(MAXN,ans.Maxn);
        FLAG=FLAG&&ans.Flag;
    }

    return ans=(hp){MAXN,FLAG}; 
}

int main(){
    n=in();
    a[0].year=-INF;
    for (int i=1;i<=n;++i){
        a[i].year=in()+INF; a[i].val=in();
        pd[a[i].year]=true;
        if (a[i].year==a[i-1].year+1) a[i].flag=true;
        else a[i].flag=false;
    }
    m=in();
    for (int i=1;i<=m;++i){
        ask[i].y=in()+INF; ask[i].x=in()+INF;
        a[++n].year=ask[i].y;
        a[n].val=a[n].flag=0;
        a[++n].year=ask[i].x;
        a[n].val=a[n].flag=0;
    }
    sort(a+1,a+n+1,cmp);
    for (int i=1;i<=n;++i)
      if (a[i].year!=a[i-1].year){
        hash[a[i].year]=++N;
        val[N]=a[i].val;
        flag[N]=a[i].flag;
      }
    build(1,1,N);

    for (int i=1;i<=m;++i){
        y=ask[i].y; x=ask[i].x;
        lrange=hash[y]; rrange=hash[x];
        bool pd1=pd[y],pd2=pd[x];

        if (lrange>rrange){
            printf("false\n");
            continue;
        }
        if (lrange==rrange){
            if (pd1) printf("true\n");
            else printf("maybe\n");
            continue;
        }
        if (lrange+1==rrange){
            if (!pd1&&!pd2) printf("maybe\n");
            else if (!pd1||!pd2) printf("maybe\n");
            else{
                if (val[lrange]<val[rrange]) printf("false\n");
                else{
                    if (y+1==x) printf("true\n");
                    else printf("maybe\n");
                }
            }
            continue;
        }

        hp ans=query(1,1,N,lrange+1,rrange-1);
        if (!pd1&&!pd2){
            printf("maybe\n");
            continue;
        }
        if (!pd1){
            if (ans.Maxn>=val[rrange]) printf("false\n");
            else printf("maybe\n");
            continue;
        }
        if (!pd2){
            if (ans.Maxn>=val[lrange]) printf("false\n");
            else printf("maybe\n");
            continue;
        }
        if (val[lrange]<val[rrange]) printf("false\n");
        else if (ans.Maxn>=val[rrange]) printf("false\n");
        else{
            if (!(ans.Flag&&flag[rrange])) printf("maybe\n");
            else printf("true\n");
        }
    }
}

总结

手残脑残毁一生。。。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值