【codeforces 19D】Points 线段树+离散化+set

D. Points
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

Pete and Bob invented a new interesting game. Bob takes a sheet of paper and locates a Cartesian coordinate system on it as follows: point (0, 0) is located in the bottom-left corner, Ox axis is directed right, Oy axis is directed up. Pete gives Bob requests of three types:
add x y — on the sheet of paper Bob marks a point with coordinates (x, y). For each request of this type it’s guaranteed that point (x, y) is not yet marked on Bob’s sheet at the time of the request.
remove x y — on the sheet of paper Bob erases the previously marked point with coordinates (x, y). For each request of this type it’s guaranteed that point (x, y) is already marked on Bob’s sheet at the time of the request.
find x y — on the sheet of paper Bob finds all the marked points, lying strictly above and strictly to the right of point (x, y). Among these points Bob chooses the leftmost one, if it is not unique, he chooses the bottommost one, and gives its coordinates to Pete.

Bob managed to answer the requests, when they were 10, 100 or 1000, but when their amount grew up to 2·105, Bob failed to cope. Now he needs a program that will answer all Pete’s requests. Help Bob, please!

Input

The first input line contains number n (1 ≤ n ≤ 2·105) — amount of requests. Then there follow n lines — descriptions of the requests. add x y describes the request to add a point, remove x y — the request to erase a point, find x y — the request to find the bottom-left point. All the coordinates in the input file are non-negative and don’t exceed 109.

Output

For each request of type find x y output in a separate line the answer to it — coordinates of the bottommost among the leftmost marked points, lying strictly above and to the right of point (x, y). If there are no points strictly above and to the right of point (x, y), output -1.

Examples
Input

7
add 1 1
add 3 4
find 0 0
remove 1 1
find 0 0
add 1 1
find 0 0

Output

1 1
3 4
1 1

Input

13
add 5 5
add 5 6
add 5 7
add 6 5
add 6 6
add 6 7
add 7 5
add 7 6
add 7 7
find 6 6
remove 7 7
find 6 6
find 4 4

Output

7 7
-1
5 5

题目大意:有n次操作,add是向坐标系中添加一个点,remove是在坐标系中删除一个点,find是对某个点进行查询,得到另一个点。要求如下:
1、所得到的点必须在查找的点的右上方(不能是正右或者正上)。
2、如果在右上方有多个点,那么选取最左边的那个。
3、如果最左侧有多个点,那么选取最下侧的那个。
4、如果没有满足条件的点,输出-1。

每个点的x、y坐标的数据范围是1e9,而操作只有2*10^5,不难想到首先就是离散化。在读入所有操作的过程中储存每一个点的x轴坐标,排序,去重,得到m(m<=n)个点,然后对这m个点建树。

我们对每个叶子节点用一个set集合来维护,它存储的是当前点对应的x轴坐标上的每一个点的y值,并且是从小到大排好序的。那么这样看来,上面写到的第三条要求就很容易达到了。只要我们能找出我们要的那个点的x轴坐标,就可以用一个upper_bound找到第一个在y轴坐标上符合要求的点了。那么问题就变成了:如何确定那个点的x轴坐标呢?

要确定x轴坐标,首先要保证这个要找的坐标上有一个比查找的那个点更高的点。而使用set可以帮助我们找出这个x轴坐标上最高的那个点的位置。

于是就可以用线段树来维护某个区间上最高的点的y轴坐标所在了。只要在查找点的右侧有一个比它更高的点,就可以确保能够得到答案。

查询的过程中,查询的是该点的右侧中,哪个点所对应的x轴坐标上有一个y轴坐标更高的点,并且是最左侧的,因此使用线段树查询的时候就需要返回第一个答案,而不再考虑更右侧的。

注意:有一个问题,就是如果find x y中的x的坐标正好是最右侧的,那么查询时要特判返回-1。

AC代码:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<set>
using namespace std;
struct node
{
    char c[10];
    int mx,my;
}k[800100];//存储操作
set<int>se[200100];//每个叶子节点的set
int x[200100];//存储离散化的x坐标
int y[800100];//存储树上每个区间的最大y坐标
void build_tree(int rt,int l,int r)
{
    y[rt]=-1;
    if(l==r)return ;
    int mid=(l+r)/2;
    build_tree(rt*2,l,mid);
    build_tree(rt*2+1,mid+1,r);
    return ;
}
void update_tree(int rt,int left,int right,int l)
{
    if(left==right)
    {
        if(se[l].size())
            y[rt]=*(--se[l].end());
        else
            y[rt]=-1;
        return ;
    }
    int mid=(left+right)/2;
    if(l<=mid)update_tree(rt*2,left,mid,l);
    else if(l>=mid+1)update_tree(rt*2+1,mid+1,right,l);
    y[rt]=max(y[rt*2],y[rt*2+1]);
    return ;
}
int query_tree(int rt,int left,int right,int l,int r,int yy)
{
    if(l>r||y[rt]<=yy)return -1;
    if(left==right) return left;
    int mid=(left+right)/2;
    if(r<=mid)return query_tree(rt*2,left,mid,l,r,yy);
    else if(l>=mid+1)return query_tree(rt*2+1,mid+1,right,l,r,yy);
    else
    {
        int t=query_tree(rt*2,left,mid,l,r,yy);
        if(t!=-1)return t;
        return query_tree(rt*2+1,mid+1,right,l,r,yy);
    }
}
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        for(int i=1;i<=n;i++)
        {
            scanf("%s %d %d",k[i].c,&k[i].mx,&k[i].my);
            x[i]=k[i].mx;
        }
        sort(x+1,x+n+1);
        int cntx=unique(x+1,x+1+n)-(x+1);
        for(int i=1;i<=cntx;i++)
        {
            se[i].clear();
        }
        build_tree(1,1,cntx);
        for(int i=1;i<=n;i++)
        {
            int l=lower_bound(x+1,x+cntx+1,k[i].mx)-x;
            if(k[i].c[0]=='a')
            {
                se[l].insert(k[i].my);
                update_tree(1,1,cntx,l);
            }
            else if(k[i].c[0]=='r')
            {
                se[l].erase(k[i].my);
                update_tree(1,1,cntx,l);
            }
            else if(k[i].c[0]=='f')
            {
                int s=query_tree(1,1,cntx,l+1,cntx,k[i].my);
                if(s==-1)printf("-1\n");
                else
                {
                    printf("%d %d\n",x[s],*se[s].upper_bound(k[i].my));
                }
            }
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值