洛谷P4422 [COCI2017-2018#1] Deda

题目

一道比较显然的线段树,因为我们要求大于等于 \(B\) 且在第 \(Y\) 站(包含第 \(Y\) 站)以前下车的最年轻的小孩是多大,所以我们可以发现把年龄作为下角标,存是在第几站下的车,每次查找年龄为 \(B\) ~ \(N\) 中的第一个在小于等于 \(Y\) 站下车的小孩的年龄。

我们用 \(mins_{rt}\) 存以 \(rt\) 为根的子树中值最小的是几,然后每次查找就先找出每个合法子树,再在这个子树中判断左子树的最小值是否合法,如果合法就进左子树,否则,进右子树,最后返回下角标,因为我们要找最小的,所以从左子树开始,之后再写一个单点修改就可以了。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#define N 200010
#define INF 0x3f3f3f3f

using namespace std;

int mins[N<<2],ans;

void pushup(int rt)
{
    mins[rt]=min(mins[rt<<1],mins[rt<<1|1]);
}


void update(int p,int x,int l,int r,int rt)    //单点修改:mins[p]=x 
{
    if(l==r)
    {
        mins[rt]=x;
        return;
    }
    int mid=(l+r)>>1;
    if(p<=mid) update(p,x,l,mid,rt<<1);
    else update(p,x,mid+1,r,rt<<1|1);
    pushup(rt);
}

int Find(int x,int l,int r,int rt)    //查找以rt为根的子树中的第一个<=x的下角标 
{
    if(mins[rt]>x) return INF;
    else if(l==r) return l;
    int mid=(l+r)>>1;
    if(mins[rt<<1]<=x) return Find(x,l,mid,rt<<1);
    else return Find(x,mid+1,r,rt<<1|1);
}

void query(int L,int R,int x,int l,int r,int rt)    //在整个树中查找范围在L-R内的子树 
{
    if(L<=l&&r<=R)
    {
        ans=Find(x,l,r,rt);
        return;
    }
    int mid=(l+r)>>1;
    if(L<=mid)
    {
        query(L,R,x,l,mid,rt<<1);
        if(ans!=INF) return;
    }
    if(mid<R) query(L,R,x,mid+1,r,rt<<1|1); 
}

int read()    //快读 
{
    int x=0;
    char c=getchar();
    while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x;
}

int main()
{
    int n=read(),m=read();
    memset(mins,0x7f,sizeof(mins));
    while(m--)
    {
        char ch[5];
        scanf("%s",ch);
        int x=read(),y=read();
        if(ch[0]=='M')
            update(y,x,1,n,1);    //年龄为y的在第x站下车
        else
        {
            query(y,n,x,1,n,1);    //在y-n岁中找第一个<=x的年龄 
            if(ans==INF) puts("-1");
            else printf("%d\n",ans);
        }
    }
    return 0;
}
posted @ 2020-05-26 00:21  Acestar  阅读(334)  评论(0编辑  收藏  举报