【Codeforces Beta Round #19 D. Points】离散化+线段树

链接:

Codeforces Beta Round #19 D. Points

题意

给你一个笛卡尔坐标系,现在要支持三种操作,第一种操作是添加一个点(x,y),第二种操作是删除一个点(x,y), 第三种操作是查询严格在点(x,y)右上角的点中,横坐标最小的点,如果有多个点,选择纵坐标最小的那个。

做法

首先题中给出的坐标范围都是1e9,所以需要对x轴进行离散化,建立线段树,每个节点存储区间内所有点中y的最大值,所以叶子节点存储的是当前x下y的最大值。 之后对于每个x开一个set存储横坐标为x的所有y。
之后对于操作1,2,我们只需要单点更新,并且在set中进行insert和erase。
对于操作3,我们首先转换为经典问题,求x+1到inf区间内最小的下标满足权值大于y,我们只要在线段树上优先看左子树是否有满足条件的点,转换为自问题递归求解即可,注意要用区间max进行剪枝,不然复杂度会退化。
这样我们就知道最小的存在大于y的下标x,之后在x所在的set中upper_bound查找第一个大于y的值即可。

代码

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<set>
using namespace std;
const int maxn = 2e5+5;
struct T
{
    int l,r,mid;
    int val,id;
}tree[maxn<<2];
multiset<int> s[maxn<<2];
void push_up(int rt)
{
    tree[rt].val=max(tree[rt<<1].val,tree[rt<<1|1].val);
}
void build(int rt,int l,int r)
{
    tree[rt].l=l;
    tree[rt].r=r;
    tree[rt].val=0;
    if(l==r)
    {
        s[l].insert(-1);
        tree[rt].val=-1;
        return ;
    }
    int mid=tree[rt].mid=l+r>>1;
    build(rt<<1,l,mid);
    build(rt<<1|1,mid+1,r);
    push_up(rt);
    return ;
}
void update(int rt,int pos,int val,int flag)
{
    if(tree[rt].l==tree[rt].r)//递归到叶子节点更新set
    {
        if(flag==0) s[tree[rt].l].erase(val);
        else s[tree[rt].l].insert(val);
        tree[rt].id=pos;
        tree[rt].val=*(s[tree[rt].l].rbegin());
        return ;
    }
    if(pos<=tree[rt].mid) update(rt<<1,pos,val,flag);
    else  update(rt<<1|1,pos,val,flag);
    push_up(rt);
    return ;
}
int query(int rt,int val,int l,int r)
{
    if(tree[rt].l>r||tree[rt].r<l) return -1;
    if(tree[rt].l>=l&&tree[rt].r<=r)//区间最大值进行剪枝
    {
        if(tree[rt].val<=val) return -1;
    }
    if(tree[rt].l==tree[rt].r) return tree[rt].id;
    if(tree[rt].mid>=l)//优先看左区间是否有满足情况的点
    {
        int res=query(rt<<1,val,l,r);
        if(res!=-1) return res;
    }
    if(tree[rt].mid<r)
    {
        int res=query(rt<<1|1,val,l,r);
        if(res!=-1) return res;
    }
    return -1;
}
struct data
{
    int flag,x,y;
}p[maxn];
char op[maxn];
int d;
int a[maxn];//原数组
int b[maxn];//每个位置离散后的值
int c[maxn];//表示离散后为i的原来的值为c[i]
int Hash[maxn];//hash去重数组
void GetHash(int a[],int n)
{
    for(int i=1;i<=n;i++) Hash[i]=a[i];
    sort(Hash+1,Hash+1+n);
    d=unique(Hash+1,Hash+1+n)-Hash-1;
    for(int i=1;i<=n;i++)
    {
        b[i]=lower_bound(Hash+1,Hash+1+d,a[i])-Hash;
        c[b[i]]=a[i];
    }
}
int main()
{
    int n,x,y;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%s%d%d",	op,&x,&y);
        if(op[0]=='a') p[i].flag=1;
        else if(op[0]=='r') p[i].flag=2;
        else p[i].flag=3;
        p[i].x=x;
        p[i].y=y;
        a[i]=x;
    }
    GetHash(a,n);//对x轴进行离散化
    build(1,1,d);//对x轴建立线段树
    for(int i=1;i<=n;i++)
    {
        if(p[i].flag==1) update(1,b[i],p[i].y,1);
        else if(p[i].flag==2) update(1,b[i],p[i].y,0);
        else
        {
            int res=query(1,p[i].y,b[i]+1,d);
            if(res==-1) printf("-1\n");
            else
            {
                set<int>::iterator it = s[res].upper_bound(p[i].y);
                printf("%d %d\n",c[res],(*it));
            }
        }
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值