洛谷题单指南-线段树-Points

原题链接:https://www.luogu.com.cn/problem/CF19D

题意解读:坐标系支持集中操作:1.添加一个点(x,y),保证不会重复 2.删除一个点(x,y) 3.查询刚好比一个点(x,y)的x,y都大的点,优先看刚好比x大的位置,如果该位置有多个点,取y最小的一个,找到则输出点的坐标,找不到则输出-1。

解题思路:

首先,可以将所有x对应的y都存下来,由于需要支持添加、删除、找比某个数大的第一个数,可以想到平衡树,set即可支持这些操作。

set<int> xys[N]; //xys[i]存x坐标是i的点对应的所有y坐标

由于x坐标的取值范围较大,可能导致数组爆内存,因此要首先对x坐标进行离散化处理,离散化后最大的x记为maxx。

其次,要实现查询操作find x y,

第一步、可以找到第一个比x大的有合适点的横坐标x',什么叫有合适点?在区间x+1 ~ maxx范围内,找到第一个有最大纵坐标大于y的位置。

既然是区间查询,不难想到可以用线段树来维护所有点x坐标区间最大的y值,定义为

struct Node
{
    int l, r;
    int maxy; //区间[l,r]范围内的点的最大y值
} tr[N * 4];

这样以来,通过在线段树中查询区间xleft ~ N范围内,第一个maxy > y的叶子结点的位置即找到了上述中的x'。

第二步、通过在xys[x']中二分找到第一个比y大的值,即可得到刚好比x,y都大的点坐标。

最后,在添加、删除点的过程中,要同时维护线段树以及xys,可以先更新xys,再更新线段树。

100分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 200005;

struct Action
{
    string op;
    int x, y;
} acts[N]; //保存所有的操作

struct Node
{
    int l, r;
    int maxy; //区间[l,r]范围内的点的最大y值
} tr[N * 4]; //对横坐标范围建立线段树

set<int> xys[N]; //xys[i]存x坐标是i的点对应的所有y坐标
vector<int> allx; //所有横坐标,用于离散化
int n;

void pushup(int u)
{
    tr[u].maxy = max(tr[u << 1].maxy, tr[u << 1 | 1].maxy);
}

void build(int u, int l, int r)
{
    tr[u] = {l, r};
    if(l == r) tr[u].maxy = -1; //设置默认值
    else
    {
        int mid = l + r >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
        //不需要pushup
    }
}

void update(int u, int pos, int val)
{
    if(tr[u].l == tr[u].r) tr[u].maxy = val;
    else
    {
        int mid = tr[u].l + tr[u].r >> 1;
        if(pos <= mid) update(u << 1, pos, val);
        else update(u << 1 | 1, pos, val);
        pushup(u);
    }
}

//在[l,r]区间查找第一个maxy > val的叶子节点的位置
int query(int u, int l, int r, int val)
{
    //在[l,r]区间找到一个叶子结点满足要求
    if(tr[u].l == tr[u].r && tr[u].l >= l && tr[u].r <= r && tr[u].maxy > val) return tr[u].l;
    //该结点maxy不满足要求,或者区间无交
    if(tr[u].l > r || tr[u].r < l || tr[u].maxy <= val) return -1;

    int left =  query(u << 1, l, r, val); 
    if(left != -1) return left; //先看左子节点是否满足要求
    else return query(u << 1 | 1, l, r, val); //再看右子节点
}

int main()
{
    cin.tie(0); cout.tie(0); ios::sync_with_stdio(false);
    cin >> n;
    for(int i = 1; i <= n; i++)
    {
        cin >> acts[i].op >> acts[i].x >> acts[i].y; //保存所有操作
        allx.push_back(acts[i].x); //提取横坐标
    }

    sort(allx.begin(), allx.end()); //排序
    allx.erase(unique(allx.begin(), allx.end()), allx.end()); //去重
    int maxx = allx.size(); //离散化后最大的横坐标

    build(1, 1, maxx); //构建线段树

    for(int i = 1; i <= n; i++)
    {
        //用离散化后的值替换横坐标
        acts[i].x = lower_bound(allx.begin(), allx.end(), acts[i].x) - allx.begin() + 1;

        if(acts[i].op == "add")
        {
            xys[acts[i].x].insert(acts[i].y); //保存x对应的y
            int maxy = *xys[acts[i].x].rbegin(); //取x对应最大的y
            if(acts[i].y == maxy) //添加新y成最大才要更新
                update(1, acts[i].x, maxy); //单点更新x的maxy
        }
        else if(acts[i].op == "remove")
        {
            xys[acts[i].x].erase(acts[i].y); //删除x对应的y
            int maxy = -1;
            if(xys[acts[i].x].size())
                maxy = *xys[acts[i].x].rbegin(); //取x对应最大的y
            if(acts[i].y > maxy) //删除的y是最大的才要更新
                update(1, acts[i].x, maxy); //单点更新x的maxy
        }
        else
        {   
            //在[acts[i].x + 1, maxx]范围找到第一个maxy>acts[i].y的叶子节点的位置
            int x = query(1, acts[i].x + 1, maxx, acts[i].y); 
            if(x == -1) cout << -1 << endl;
            else
            {
                //在x对应的所有y中找第一个比acts[i].y大的值
                int y = *xys[x].upper_bound(acts[i].y);
                cout << allx[x-1] << " " << y << endl; //x要还原为离散化之前的值
            }
        }
    }

    return 0;
}

 

posted @ 2024-12-19 19:20  五月江城  阅读(4)  评论(0编辑  收藏  举报