CF 19D - Points (线段树+set)

该博客介绍了如何处理二维坐标系中包含加点、删点和查询的操作。通过离线处理、操作去重和构建线段树,结合集合数据结构,实现对操作的高效管理。线段树维护排序后的最大y坐标,查询时根据点的相对位置进行二分查找,以确定符合条件的点坐标。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意
给你一个2维坐标系,之后给你n(2e5)次操作,有加点操作,删点操作,和查询操作,加和删就是在二维坐标系里加一个点和删一个点,其中查询点是给你一个(x,y)让你找到一个(x1,y1),(x1>x,y1>y)如果有找到最小的x1,如果x1相同找到最小的y1。
思路
首先n有2e5,我们首先离线我们的所有操作,去重后建一棵长度为n的线段树,我们把他是第几大当作他的区间数,之后我们的线段树维护的是排序离散后第i大的最大的y,那么对于一个加操作或者删操作,我们可以放进set里就好了,加我们就把数放到set里,如果删除点我们就erase他就好了。之后在查询(x,y)的时候,我们先看他是第几大的(设为L),那么我们在线段树中查询区间(L+1,n)看他维护的节点的最大值和我们的y比较,如果满足维护的节点大于我们的y坐标就证明我们找到了这样一个x坐标,之后我们在set[x]里二分找就好了。总结一下就是:我们离散过后按照大小顺序建一棵(1,n)的线段树,他们的节点维护的就是,以x为坐标的最大的y,之后对于一个查询(x,y)我们先看他是第几大(设为L),之后在线段树中查找(L+1,n),看他所维护的最大值Y是否满足我们查询的y,之后二分找就行了

#include <bits/stdc++.h>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
const int maxn = 2e5+10;
char ch[maxn][20];
int x[maxn],l[maxn],r[maxn];
int seg[maxn<<2];
set<int>S[maxn];
void pushup(int rt)
{
    seg[rt] = max(seg[rt<<1] , seg[rt<<1|1]);
}
void build(int l,int r, int rt)
{
    seg[rt] = -1;
    if(l == r) return ;
    int m = (l+r)>>1;
    build(lson);
    build(rson);
}
void update(int pos,int l,int r,int rt)
{
    if(l == r)
    {
        seg[rt] = ((int)S[pos].size()==0)?-1:(*(--S[pos].end()));
        return ;
    }
    int m = (l+r)>>1;
    if(pos <= m) update(pos,lson);
    else update(pos,rson);
    pushup(rt);
}
int query(int x ,int L ,int R, int l,int r,int rt)
{
    if(L>R) return -1;
    if(x >= seg[rt]) return -1;
    if(l == r) return l;
    int m = (l+r)>>1;
    if(R <= m) return query(x,L,R,lson);
    else  if(L > m) return query(x,L,R,rson);
    else
    {
        int te = query(x,L,m,lson);
        if(te >= 0) return te;
        return query(x,m+1,R,rson);
    }
}
void print(int l,int r,int rt)
{
    if(l == r)
    {
        cout<<seg[rt]<<endl;
        return ;
    }
    int m = (l+r)>>1;
    print(lson);
    print(rson);
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i = 0 ; i < n ; i++)
    {
        scanf("%s %d%d",ch[i],&l[i],&r[i]);
        x[i] = l[i];
    }
    sort(x,x+n);
    int cnt = unique(x,x+n) - x;
    build(1,cnt,1);
    for(int i = 0 ; i < n ; i++)
    {
        int L = lower_bound(x,x+cnt,l[i]) - x + 1; // 这里的x是从0开始的,而我们的线段树是没有0节点的,所以我们要让节点+1
        if(ch[i][0] == 'a')
        {
          //  printf("%d %d\n",L,r[i]);
            S[L].insert(r[i]);
            update(L,1,cnt,1);
        }
        else if(ch[i][0] == 'r')
        {
            S[L].erase(S[L].find(r[i]));
            update(L,1,cnt,1);
        }
        else if(ch[i][0] == 'f')
        {
            int P = query(r[i],L+1,cnt,1,cnt,1);
            if(P == -1) puts("-1");
            else
            {
                printf("%d %d\n",x[P-1],*(S[P].upper_bound(r[i])));
            }
        }
    }
  //  print(1,cnt,1);
}
/*
10
add 5 7
add 2 1
add 8 8
add 5 10
add 2 5
find 7 5
find 8 3
find 2 2
find 5 4
find 2 6

*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值