首页 > 其他分享 >洛谷题单指南-线段树-Points

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

时间:2024-12-19 19:33:07浏览次数:3  
标签:maxy int 洛谷题 线段 acts tr Points xys allx

原题链接: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;
}

 

标签:maxy,int,洛谷题,线段,acts,tr,Points,xys,allx
From: https://www.cnblogs.com/jcwy/p/18604456

相关文章

  • javascript 两点之间的积分点数(Number of Integral Points between Two Points)
    给定两点p(x1,y1)和q(x2,y2),计算连接它们线上的积分点的数量。输入:x1=2,y1=2,x2=5,y2=5输出:2解释:连接(2,2)和(5,5)的线上只有2个整数点。这两个点是(3,3)和(4,4)。输入:x1=1,y1=9,x2=8,y2=16输出:6解释:连接(1,9)和(8,16)的线上有6个整数......
  • 2024-12-18:正方形中的最多点数。用go语言,给定一个二维数组 points 和一个字符串 s,其中
    2024-12-18:正方形中的最多点数。用go语言,给定一个二维数组points和一个字符串s,其中points[i]表示第i个点的坐标,s[i]表示第i个点的标签。如果一个正方形的中心在(0,0),边与坐标轴平行,并且内部没有标签相同的两个点,则称这个正方形为“合法”的。你的任务是返回可以被“合......
  • zkw线段树学习笔记
    先%一下zkw。stozkworzzkw线段树是一个改良版的线段树。其功能与传统线段树相同,也是用于维护区间信息。但是zkw线段树有很多优点:代码简短;纯天然非递归;常数小(尤其在差分区间更新时)特点:堆式储存,找父亲只需右移一位。建树和线段树一样,父节点左移一位为左儿子,再+1(或者|......
  • 在一个svg里进行大量线段的绘制,请问有没有什么可以提高性能的办法,类似 winform里的Sus
    在前端开发中,尤其是在处理SVG图形和大量线段绘制时,性能优化是非常重要的。虽然不像WinForms中的`SuspendLayout`和`ResumeLayout`那样直接控制布局更新的暂停与恢复,但在Web环境中也有多种方法可以提高SVG渲染性能。以下是几种常见的优化策略:###1.使用批量更新尽量减少DOM操作......
  • Qt/C++地图测距/显示不同线段的距离/拿到测距结果/测距结束信号
    一、前言说明地图测距在地图组件中属于一个比较小众的功能,但是又不得不提供,有时候用户希望直接在地图上选点,测算距离,尤其是在一些军事领域用的比较多,测距功能提炼出来的共性就是,每一段都有距离,最后鼠标右键或者双击结束测距,然后发个信号传过来总的距离。一般地图厂家也都提供了对......
  • 线段树进阶
    线段树分治(时间线段树)线段树分治是一种离线的算法,按时间分治。常用于处理每个操作有一定的生效时间(或者每个查询限制一段时间)的题目。其本质是钦定良好的顺序来得出答案,使得执行操作的次数最少。而对于具有类似思想的trick有:对于一些图论问题,可以将操作离线,然后对于每一个操......
  • 洛谷题单指南-线段树-P4145 上帝造题的七分钟 2 / 花神游历各国
    原题链接:https://www.luogu.com.cn/problem/P4145题意解读:对于序列a[n],支持两种操作:1.对区间[l,r]内每个数开方2.查询区间[l,r]每个数的和解题思路:区间修改,区间查询,可以用线段树解决。咋一看,需要借助于懒标记来修改节点,但仔细分析,开方操作并不具备可累加性,并且也不能通过开方......
  • [HBCPC2024] Points on the Number Axis A
    [HBCPC2024]PointsontheNumberAxisA题目描述Aliceisplayingasingle-playergameonthenumberaxis.Thereare\(n\)pointsonthenumberaxis.Eachtime,theplayerselectstwopoints.Thetwopointswillberemoved,andtheirmidpointwillbeadded.......
  • 洛谷题单指南-线段树-P5522 [yLOI2019] 棠梨煎雪
    原题链接:https://www.luogu.com.cn/problem/P5522题意解读:有若干0/1/?组成的字符串,支持两种操作:1.将制定位置字符串修改成新字符串;2.查询区间内字符串能否统一成一个字符串,求有多少种可能;将2的所有结果异或起来,再和0异或,输出最终答案。注意:?表示可以用0或1取代。解题思路:单点修......
  • 洛谷题单python解【入门2】分支结构
    P2433【深基1-2】小学数学N合一importmathn=int(input())ifn==1:print('IloveLuogu!')elifn==2:print(2+4,10-2-4)elifn==3:print(14//4)print(14-14%4)print(14%4)elifn==4:print("%.3f"%(500/3))elifn==5:......