CF 19D - Points 线段树套平衡树

题目在这:

给出三种操作:
1.增加点(x,y)
2.删除点(x,y)
3.询问在点(x,y)右上方的点,如果有相同,输出最左边的,如果还有相同,输出最低的那个点

分析:
线段树套平衡树。
我们先离散化输入的x坐标,然后以每个坐标建立一棵平衡树来维护,这里可以直接用set或者map来维护就行了。

然后我们现在需要在x的右方找到最左最下大于(x,y)的点。
建立一棵线段树,维护的是区间的纵坐标的最值,而线段树的端点为离散后x的值。

1.我们每次插入的时候,直接在相应的平衡树中插入,然后更新一下线段树的区间最值。
2.删除时,直接删掉,更新一下最值。
3.询问时,对于整个区间,我们找到比x大的区间,利用区间最值,从左往右找是否存在大于y的点。最后输出即可。

具体看代码。。。

 

#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

#define debug puts("here")
#define rep(i,n) for(int i=0;i<n;i++)
#define rep1(i,n) for(int i=1;i<=n;i++)
#define REP(i,a,b) for(int i=a;i<=b;i++)
#define foreach(i,vec) for(unsigned i=0;i<vec.size();i++)
#define pb push_back
#define RD(n) scanf("%d",&n)
#define RD2(x,y) scanf("%d%d",&x,&y)
#define RD3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define RD4(x,y,z,w) scanf("%d%d%d%d",&x,&y,&z,&w)
#define All(vec) vec.begin(),vec.end()
#define MP make_pair
#define PII pair<int,int>

/******** program ********************/

const int MAXN = 2e5+5;

int id[MAXN];
set<int> se[MAXN];

struct Data{
    char op;
    int x,y;
}d[MAXN];

struct node{
    int l,r,mx;
    inline int mid(){
        return (l+r)>>1;
    }
}tree[MAXN<<2];

void build(int l,int r,int rt){ // 建树
    tree[rt].l = l;
    tree[rt].r = r;
    tree[rt].mx = -1;
    if(l==r){
        se[l].clear();	// 平衡树清空
        return;
    }
    int mid = tree[rt].mid();
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
}

void update(int x,int rt){ // 更新操作
    if(tree[rt].l==tree[rt].r){
        if(se[x].size()==0) // 如果为空
            tree[rt].mx = -1;
        else{
            set<int>::iterator it = se[x].end(); // 最大值其实就是最后一个元素
            tree[rt].mx = *--it;
        }
        return;
    }
    int mid = tree[rt].mid();
    if(x<=mid)   update(x,rt<<1);
    else        update(x,rt<<1|1);
    tree[rt].mx = max(tree[rt<<1].mx,tree[rt<<1|1].mx);
}

int ask(int x,int y,int rt){
    if(tree[rt].l==tree[rt].r)
        return tree[rt].l;
    if( tree[rt<<1].r>x && tree[rt<<1].mx>y ){ // 如果左儿子区间存在比x大且最值大于y,可能在左儿子区间
        int t = ask(x,y,rt<<1);
        if(t!=-1)   return t;
    }
    if( tree[rt<<1|1].mx>y ) // 这里右儿子的右端点显然比x大
        return ask(x,y,rt<<1|1);
    return -1;
}

int main(){

#ifndef ONLINE_JUDGE
	freopen("sum.in","r",stdin);
	//freopen("sum.out","w",stdout);
#endif

    int n;
    int ncase = 0;
    while(cin>>n){
        ncase?puts("----------------"):ncase = 1;
        char op[10];
        vector<int> vec;
        rep1(i,n){
            scanf("%s%d%d",op,&d[i].x,&d[i].y);
            d[i].op = op[0];
            vec.pb(d[i].x);
        }
        sort(All(vec));
        vec.erase( unique(All(vec)),vec.end() ); // 去重
        foreach(i,vec)
            id[i] = vec[i];
        int len = vec.size();

        build(0,len+10,1);

        rep1(i,n){
            int x = lower_bound(id,id+len,d[i].x)-id+1; // 二分出x离散后的值
			
            int y = d[i].y;
            if(d[i].op=='a'){
                se[x].insert(y);
                update(x,1);
            }
            else if(d[i].op=='r'){
                se[x].erase(y);
                update(x,1);
            }
            else{
                int t = ask(x,y,1);
                if(t==-1)
                    puts("-1");
                else
                    printf("%d %d\n",id[t-1],*se[t].upper_bound(y)); // 二分出比y大的值
            }
        }
    }


	return 0;
}

  

 

转载于:https://www.cnblogs.com/yejinru/p/3155834.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值