codeforces 19D(线段树端点维护set 或脑补CDQ?)

本文介绍了一种解决二维平面上点集查询问题的方法,通过离散化横坐标,利用线段树和set数据结构,高效处理点的添加、删除及查询严格右上方最近点的操作,适用于操作数量达2*10^5次的场景。

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

题意:二维平面三种操作,添加一个点,删除一个点,询问一个点的严格右上方中离他最近的点,若有多个则横坐标小的优先,操作次数 2 ∗ 1 0 5 2*10^5 2105,保证操作合法。

对横坐标离散化后,线段树叶子节点建立set,存放y坐标,那么增与删都很好做,同时线段树维护y坐标最大值,查询的时候如果左右子树同时都可能满足,则优先查询左子树,若找不到答案再查右子树,查询过程中同时使用max进行搜索剪枝。

#include <bits/stdc++.h>
using namespace std;
 
typedef long long ll;
 
const int maxn=2e5+7;
 
set<int> s[maxn<<2|1];
 
int maxx[maxn<<2|1];
 
void build(int l,int r,int k){
    maxx[k]=-1;
    if(l==r) return ;
    int mid=(l+r)>>1;
    build(l,mid,k<<1);
    build(mid+1,r,k<<1|1);
}
 
void insert(int l,int r,int k,int id,int val){
    if(l==r){
        s[k].insert(val);
        maxx[k]=*(--s[k].end());
        return ;
    }
    int mid=(l+r)>>1;
    if(id<=mid) insert(l,mid,k<<1,id,val);
    else insert(mid+1,r,k<<1|1,id,val);
    maxx[k]=max(maxx[k<<1],maxx[k<<1|1]);
}
 
void erase(int l,int r,int k,int id,int val){
    if(l==r){
        s[k].erase(s[k].find(val));
        if(s[k].empty()) maxx[k]=-1;
        else maxx[k]=*(--s[k].end());
        return ;
    }
    int mid=(l+r)>>1;
    if(id<=mid) erase(l,mid,k<<1,id,val);
    else erase(mid+1,r,k<<1|1,id,val);
    maxx[k]=max(maxx[k<<1],maxx[k<<1|1]);
}
 
int res=-1;
int ask(int l,int r,int k,int id,int val){
    if(maxx[k]<val) return -1;
    if(l==r){
        if(s[k].empty()) return -1;
        set<int>::iterator it=s[k].upper_bound(val);
        if(it==s[k].end()) return -1;
        res=l;
        return (*it);
    }
    int mid=(l+r)>>1;
    if(id>mid) return ask(mid+1,r,k<<1|1,id,val);
    else{
        int hh=-1;
        hh=ask(l,mid,k<<1,id,val);
        if(hh!=-1) return hh;
        return ask(mid+1,r,k<<1|1,id,val);
    }
}
 
int b[maxn];
int m;
void quchong(int n){
    sort(b+1,b+1+n);
    m=unique(b+1,b+1+n)-b-1;
}
int getid(int x){ return lower_bound(b+1,b+1+m,x)-b; }
 
struct Node{
    int type; //0 add  1 erase 2 ask
    int x,y;
}a[maxn];
char ss[9];
int main(){
    int q,num=0;
    scanf("%d",&q);
    for(int i=1;i<=q;++i){
        scanf("%s%d%d",ss,&a[i].x,&a[i].y);
        b[i]=a[i].x;
        if(ss[0]=='a') a[i].type=0;
        else if(ss[0]=='r') a[i].type=1;
        else a[i].type=2;
    }
    quchong(q);
    b[++m]=b[m-1]+1;
    //for(int i=1;i<=m;++i) cout<<b[i]<<" ";cout<<endl;
    build(1,m,1);
    for(int i=1;i<=q;++i){
        if(a[i].type==0) insert(1,m,1,getid(a[i].x),a[i].y);
        else if(a[i].type==1) erase(1,m,1,getid(a[i].x),a[i].y);
        else{
            res=-1;
            int hh=ask(1,m,1,getid(a[i].x)+1,a[i].y);
            if(res==-1) printf("-1\n");
            else{
                printf("%d %d\n",b[res],hh);
            }
        }
    }
 
 
    return 0;
}

再来一种我自己yy的做法,现在还没有调过去2333。
用CDQ将动态问题转化为静态问题,三种操作,删除优于添加,然后是查询,按照x坐标从大到小,y从大到小,同时树状数组维护y坐标下的x+y最小,同时标记这个最小值的点的所属。

写完再更。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值