洛谷 P4148:简单题 ← KD-Tree模板题

news2025/1/8 20:29:53

【题目来源】
https://www.luogu.com.cn/problem/P4148

【题目描述】
你有一个 N×N 的棋盘,每个格子内有一个整数,初始时的时候全部为 0,现在需要维护两种操作:
● 1 x y A → 1≤x,y≤N,A 是正整数。将格子 (x,y) 里的数字加上 A。
● 2 x1 y1 x2 y2 → 1≤x1≤x2≤N,1≤y1≤y2≤N。输出 x1,y1,x2,y2 这个矩形内的数字和
● 3 → 无终止程序

【输入格式】
输入文件第一行一个正整数 N。
接下来每行一个操作。每条命令除第一个数字之外,均要异或上一次输出的答案 last_ans,初始时 last_ans=0。

【输出格式】
对于每个 2 操作,输出一个对应的答案。

【输入样例】
4
1 2 3 3
2 1 1 3 3
1 1 1 1
2 1 1 0 7
3

【输出样例】
3
5


【说明/提示】
1≤N≤5×10^5,操作数不超过 2×10^5 个,内存限制 20MB,保证答案在 int 范围内并且解码之后数据仍合法。

【算法分析】
● KD-Tree 作为一种
二分查找树,由 Jon Louis Bentley 发明。所谓 KD-Tree,即 Multidimensional Binary Search Tree 的简称,其中的 K 表示数据空间的维度。KD-Tree 高效支持高维空间的最近邻搜索空间搜索,因此深入了解 KD-Tree 的原理以及底层实现,对机器人或无人驾驶领域的从业者来说,意义非凡。

● KD-Tree 的基本操作包括
维度划分建树插入新结点删除结点寻找第 i 维的最小值等。
其中,维度划分常用
轮转法最大方差法
(1)轮转法,即按维度交替划分。例如,若共有 K 个维度。那么,第 dep 层按 dep%K 划分,第 dep+1 层按 (dep+1)%K 划分,……。轮转法适用于所有维度的数据分布都比较均匀的情形。特别地,
针对二维平面,按 x,y 交替划分,奇数层按 x 划分,偶数层按 y 划分。
(2)最大方差法,适用于某些维度的值变化不大的情况。例如,平面上呈现为一条横线的 n 个点,x 值分布均匀,y 值相差很小,若按 y 划分没有太大意义,故选方差最大的维度 x 进行划分。方差的计算公式为:s^2=\frac{1}{n}\sum_{i=1}^{n}(x_i-\mu)^2,其中 μ 为 x 的均值。
无论采用哪种划分方法对某个维度进行划分,一般选取此维度的
中位数作为划分点,并将其作为二叉树某个子树的根。中位数常用 STL 中的 nth_element() 函数直接得到。

在数学中,给定 n 个数,当 n 为偶数时,中位数为位序 n/2+1 对应的数;当 n 为奇数时,中位数为位序 (n+1)/2 对应的数。例如:
求 (23、29、20、32、23、21、33、25) 及 (30、20、 20、 20、 10) 的中位数。
解:对 (23、29、20、32、23、21、33、25) 排序后,得 (20、21、23、23、25、29、32、33)。
由于此题中 n=8,故中位数为位序 n/2+1=8/2+1=5 对应的数25。
对 (30、20、 20、 20、 10) 排序后,得 (10、20、 20、 20、 30) 。
由于此题中 n=5,故中位数为位序 (n+1)/2=(5+1)/2=3
对应的数20。

● 利用轮转法构建 KD-Tree 的示例如下:
给定二维平面点 (x,y) 的集合 (2,3),(5,4),(9,6),(4,7),(8,1),(7,2),利用轮转法构建 KD-Tree 的过程示例如下。
(1)构建根结点:由于
奇数层按 x 划分,故将点集 (2,3),(5,4),(9,6),(4,7),(8,1),(7,2) 按 x 值排序,得 (2,3),(4,7),(5,4)(7,2)(8,1),(9,6),其中位数位序对应的结点值为 (7,2)。即将结点 (7,2) 作为根结点,(2,3),(4,7),(5,4) 将位于 (7,2) 的左子树,(8,1),(9,6) 将位于 (7,2) 的右子树。
(2)构建左子树:由于
偶数层按 y 划分,故将点集 (2,3),(4,7),(5,4) 按 y 值排序,得 (2,3)(5,4)(4,7), 其中位数位序对应的结点值为 (5,4)。即将结点 (5,4) 作为根结点,(2,3) 将位于 (5,4) 的左子树,(4,7) 将位于 (5,4) 的右子树。
(3)构建右子树:由于
偶数层按 y 划分,故将点集 (8,1),(9,6) 按 y 值排序,得 (8,1),(9,6), 其中位数位序对应的结点值为 (9,6)。即将结点 (9,6) 作为根结点,(8,1) 将位于 (9,6) 的左子树,(9,6) 右子树为空。
(4)其他结点按上述步骤逐个添加到 KD-Tree 中。

最终构建所得的 KD-Tree 如下图所示。


 

可以看出,对二维平面构建 KD-Tree 的过程,就是将二维平面逐步划分的过程。如下图所示。

维基百科给出了对三维空间构建 KD-Tree 的过程及空间划分。首先,边框为红色的竖直平面将整个空间划分为两部分。其次,此两部分又分别被边框为绿色的水平平面划分为上下两部分。最后,此 4 个子空间又分别被边框为蓝色的竖直平面分割为两部分,变为 8 个子空间,此 8 个子空间即为叶子结点。如下图所示。


● KD-Tree 的建树插入新结点删除结点等操作,会打破 KD-Tree 的平衡。替罪羊树常用于维护 KD-Tree 的平衡。KD-Tree 当 K 等于 1 时,就是一颗替罪羊树
● 快读:
https://blog.csdn.net/hnjzsyjyj/article/details/120131534

【算法代码】

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

const int maxn=1e5+5;
const double alpha=0.75;
const int K=2;

int n;
int num;
int last,root,len;
int p[K],q[K][2];
int A,D;
int h[maxn];

struct KD_Tree {
    int le,ri;
    int sum,val,size;
    int minv[K],maxv[K],d[K];
} tr[maxn];

bool cmp(const int &a,const int &b) {
    return tr[a].d[D]<tr[b].d[D];
}

int read() { //fast read
    int x=0,f=1;
    char c=getchar();
    while(c<'0' || c>'9') { //!isdigit(c)
        if(c=='-') f=-1;
        c=getchar();
    }
    while(c>='0' && c<='9') { //isdigit(c)
        x=x*10+c-'0';
        c=getchar();
    }
    return x*f;
}

void update(int x) {
    int le=tr[x].le, ri=tr[x].ri;
    tr[x].size=tr[le].size+tr[ri].size+1;
    tr[x].sum=tr[le].sum+tr[ri].sum+tr[x].val;
    for(int i=0; i<K; i++) {
        if(le) {
            tr[x].maxv[i]=max(tr[le].maxv[i],tr[x].maxv[i]);
            tr[x].minv[i]=min(tr[le].minv[i],tr[x].minv[i]);
        }
        if(ri) {
            tr[x].maxv[i]=max(tr[ri].maxv[i],tr[x].maxv[i]);
            tr[x].minv[i]=min(tr[ri].minv[i],tr[x].minv[i]);
        }
    }
}

void build(int &x,int le,int ri,int pos) {
    if(le>ri) return;
    int mid=(le+ri)>>1;
    D=pos;
    nth_element(h+le,h+mid+1,h+ri+1,cmp);
    x=h[mid];
    tr[x].sum=tr[x].val;
    for(int i=0; i<K; i++) tr[x].maxv[i]=tr[x].minv[i]=tr[x].d[i];
    build(tr[x].le,le,mid-1,(pos+1)%K);
    build(tr[x].ri,mid+1,ri,(pos+1)%K);
    update(x);
}

void erase(int &x) {
    if(!x) return;
    h[++num]=x;
    erase(tr[x].le), erase(tr[x].ri);
    x=0;
}

void rebuild(int &x,int pos) {
    h[num=1]=++len;
    tr[len].size=1;
    for(int i=0; i<K; i++) tr[len].d[i]=p[i];
    tr[len].val=tr[len].sum=A;
    erase(x);
    build(x,1,num,pos);
}

void insert(int &x,int pos) {
    if(!x) {
        tr[x=++len].size=1, tr[x].val=tr[x].sum=A;
        for(int i=0; i<K; i++) tr[x].maxv[i]=tr[x].minv[i]=tr[x].d[i]=p[i];
        return;
    }
    if(p[pos]<tr[x].d[pos]) {
        if(tr[tr[x].le].size>tr[x].size*alpha) rebuild(x,pos);
        else insert(tr[x].le,(pos+1)%K);
    } else {
        if(tr[tr[x].ri].size>tr[x].size*alpha) rebuild(x,pos);
        else insert(tr[x].ri,(pos+1)%K);
    }
    update (x);
}

bool check_range(int x) {
    if(!x) return 0;
    for(int i=0; i<K; i++) {
        if(q[i][0]>tr[x].minv[i] || q[i][1]<tr[x].maxv[i]) return 0;
    }
    return 1;
}

bool check_point(int x) {
    if(!x) return 0;
    for(int i=0; i<K; i++) {
        if(tr[x].d[i]<q[i][0] || tr[x].d[i]>q[i][1]) return 0;
    }
    return 1;
}

bool check(int x) {
    if(!x) return 0;
    for(int i=0; i<K; i++) {
        if(q[i][1]<tr[x].minv[i] || q[i][0]>tr[x].maxv[i]) return 0;
    }
    return 1;
}

void query(int x) {
    if(check_range(x)) {
        last+=tr[x].sum;
        return;
    }
    if(check_point(x)) last+=tr[x].val;
    if(check(tr[x].le)) query(tr[x].le);
    if(check(tr[x].ri)) query(tr[x].ri);
}

int main() {
    n=read();
    while(1) {
        int op=read();
        if(op==1) {
            for(int i=0; i<K; i++) p[i]=read()^last;
            A=read()^last;
            insert(root,0);
        }

        if(op==2) {
            for(int i=0; i<=1; i++) {
                for(int j=0; j<K; j++) q[j][i]=read()^last;
            }
            last=0;
            query(root);
            printf("%d\n",last);
        }

        if(op==3) break;
    }

    return 0;
}

/*
in:
4
1 2 3 3
2 1 1 3 3
1 1 1 1
2 1 1 0 7
3

out:
3
5
*/





【参考文献】
https://www.cnblogs.com/PaulShi/p/10131078.html
https://www.cnblogs.com/flyinggod/p/8727584.html
https://www.cnblogs.com/Tenshi/p/15846105.html
https://oi-wiki.org/ds/kdt/
https://www.cnblogs.com/AWCXV/p/7632254.html
https://blog.csdn.net/qq_45323960/article/details/109698448
https://www.cnblogs.com/sitiy/p/15380688.html
https://www.luogu.com.cn/problem/solution/P4148
https://www.cnblogs.com/cmy-blog/p/kdtree.html
https://zhuanlan.zhihu.com/p/112246942
https://blog.csdn.net/hnjzsyjyj/article/details/128647972


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1653166.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

韩国站群服务器提供高级安全防护以确保数据和网站的安全性

韩国站群服务器提供高级安全防护以确保数据和网站的安全性 在当今数字化时代&#xff0c;网站的安全性和数据保护已成为企业和个人不可忽视的重要议题。韩国站群服务器作为一个拥有发达科技和互联网基础设施的国家&#xff0c;通过提供高级安全防护措施&#xff0c;为用户的数…

安卓surfaceview的使用方式

1. 什么是surfaceview surfaceview内部机制和外部层次结构 在安卓开发中&#xff0c;我们经常会遇到一些需要高性能、高帧率、高画质的应用场景&#xff0c;例如视频播放、游戏开发、相机预览等。这些场景中&#xff0c;我们需要直接操作图像数据&#xff0c;并且实时地显示到…

后端常用技能:解决java项目前后端传输数据中文出现乱码、问号问题

0. 问题背景 最近做一个解析数据的小工具&#xff0c;本地运行时都正常&#xff0c;发布到服务器上后在导出文件数据时发现中文全部变成了问号&#xff0c;特此记录下问题解决的思路和过程 1. 环境 java 1.8 springboot 2.6.13 额外引入了fastjson&#xff0c;commons-csv等…

##07 从线性回归到神经网络:PyTorch实战解析

文章目录 前言线性回归基础理论背景实现步骤神经网络介绍理论背景实现步骤从线性回归到神经网络结论前言 在深度学习的浩瀚宇宙中,线性回归和神经网络是两个基本但极其重要的概念。线性回归模型是统计学和机器学习的基石之一,而神经网络则是深度学习技术的核心。本文旨在通过…

【日志革新】在ThinkPHP5中实现高效TraceId集成,打造可靠的日志追踪系统

问题背景 最近接手了一个骨灰级的项目&#xff0c;然而在项目中遇到了一个普遍的挑战&#xff1a;由于公司采用 ELK&#xff08;Elasticsearch、Logstash、Kibana&#xff09;作为日志收集和分析工具&#xff0c;追踪生产问题成为了一大难题。尽管 ELK 提供了强大的日志分析功…

在时间同步应用上节省大量时间!德思特GNSS模拟器是怎么做到的?

​ 作者介绍 德思特Safran GNSS模拟器是一款综合解决方案&#xff0c;专为精确的PNT&#xff08;位置、导航和时间&#xff09;仿真与测试设计。它超越了传统GNSS定位导航仿真&#xff0c;也能提供极高的授时精度。 这款模拟器对于评估和提升GNSS接收机及同步系统的整体性能至…

前端JS必用工具【js-tool-big-box】,验证是否是Unicode字符,获取一个字符串的字节长度,以及新增发送JSONP跨域请求的方法

js-tool-big-box&#xff0c;目前已经收集到了用户需求&#xff0c;希望可以添加一些公用方法&#xff0c;我觉得这很好&#xff0c;我们一起把这个前端通用工具做大一些&#xff0c;帮助更多的小伙伴少些util代码&#xff0c;更多的关注于自己的业务开发&#xff0c;真是不错。…

OpenCV与AI深度学习 | 如何使用YOLOv9检测图片和视频中的目标

本文来源公众号“OpenCV与AI深度学习”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;如何使用YOLOv9检测图片和视频中的目标 1 介绍 在之前的文章中&#xff0c;我们探索了使用 YOLOv8 进行对象检测。现在&#xff0c;我们很高兴…

【仪酷LabVIEW AI工具包案例】使用LabVIEW AI工具包+YOLOv5结合Dobot机械臂实现智能垃圾分类

‍‍&#x1f3e1;博客主页&#xff1a; virobotics(仪酷智能)&#xff1a;LabVIEW深度学习、人工智能博主 &#x1f384;所属专栏&#xff1a;『仪酷LabVIEW AI工具包案例』 &#x1f4d1;上期文章&#xff1a;『【YOLOv9】实战二&#xff1a;手把手教你使用TensorRT实现YOLOv…

Spring - 7 ( 13000 字 Spring 入门级教程 )

一&#xff1a;Spring Boot 日志 1.1 日志概述 日志对我们来说并不陌生&#xff0c;我们可以通过打印日志来发现和定位问题, 或者根据日志来分析程序的运行过程&#xff0c;但随着项目的复杂度提升, 我们对日志的打印也有了更高的需求, 而不仅仅是定位排查问题 比如有时需要…

关于 c++ 中字符串 string 及 常量字符串的换行与使用

&#xff08;1&#xff09;例如 cout << " ddddddddddddddddddd" 。当输出字符太长&#xff0c;就需要换行。疑问是如何写代码&#xff0c;才可以保证源代码中的字符串换行被正确编译呢&#xff1f;测试一下&#xff0c;如下图可见&#xff0c;如此换行&#x…

STM32:GPIO输入输出

文章目录 1、GPIO介绍1.1 GPIO的基本结构1.1 GPIO的位结构 2、 GPIO工作模式3、GPIO标准外设库接口函数3.1 RCC接口函数3.2 GPIO接口函数3.2.1 GPIO的读取函数3.2.1 GPIO的写入函数 4、GPIO的初始化 1、GPIO介绍 GPIO&#xff08;General Purpose Input Output&#xff09;通用…

【MQTT】服务端、客户端工具使用记录

目录 一、服务端 1.1 下载 1.2 相关命令 &#xff08;1&#xff09;启动 &#xff08;2&#xff09;关闭 &#xff08;3&#xff09;修改用户名和密码 1.3 后台管理 &#xff08;1&#xff09;MQTT配置 &#xff08;2&#xff09;集群概览 &#xff08;3&#xff09;…

场外期权个股怎么对冲?

今天期权懂带你了解场外期权个股怎么对冲&#xff1f;场外个股期权是一种在非交易所市场进行的期权交易&#xff0c;它允许投资者针对特定的股票获得未来买入或卖出的权利。 场外期权个股怎么对冲&#xff1f; 持有相反方向的期权&#xff1a;这是最直接的对冲方法&#xff0c…

今晚 19:00 | 从这两个问题入手,带你了解数据要素相关税务问题

五一假期已经结束&#xff0c;返工后当然是继续劳动啦~数据要素系列直播《星光对话》第三期也将在今晚19:00&#xff0c;继续跟大家见面。 本期直播&#xff0c;依然由 星光数智咨询总监 刘靖 主讲&#xff0c;带来&#xff1a;《数据要素相关税务问题解读》。 主要围绕两个问题…

怎么快速分享视频文件?用二维码看视频的方法

怎样不通过传输下载分享视频内容呢&#xff1f;以前分享视频内容&#xff0c;大多会通过微信、QQ、邮箱、网盘等形式来传递。但是这种方式需要下载后才可以观看&#xff0c;不仅占用手机内存&#xff0c;而且效率也比较低&#xff0c;所以现在很多人会采用视频生成二维码的方式…

为 Flutter 应用设置主题:ThemeData 和 ColorScheme 指南

在媒体和其他来源中有许多关于这个主题的文章&#xff0c;那么这篇文章的必要性是什么&#xff1f; 在本文中&#xff0c;我计划仅关注 ThemeData 小部件的关键点以及我的开发经验中最常用的参数&#xff0c;并且您将获得有关每个参数如何对您的应用程序执行操作的简要说明。 …

LeetCode70:爬楼梯

题目描述 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 解题思想 1.确定dp数组以及下标的含义 dp[i]&#xff1a; 爬到第i层楼梯&#xff0c;有dp[i]种方法 2.确定递推公式 从dp[i]的定义可以…

旋转测径仪的常见故障和排除方法

关键字: 旋转测径仪,测径仪常见故障,测径仪故障排除方法,测径仪维护,测径仪较零 点击 “开始测量”按钮时提示“初始化失败&#xff01;”无法进行测量。 ◆ 检查控制柜面板“工作”指示灯&#xff08;绿&#xff09;是否点亮&#xff1b; ◆ 最小化软件窗口&#xff0c;查看…

解密某游戏的数据加密

前言 最近有个兄弟通过我的视频号加我&#xff0c;咨询能否将这个dubo游戏游戏开始前就将数据拿到从而进行押注&#xff0c;于是通过抓包工具测试了下&#xff0c;发现数据有时候是明文&#xff0c;有时候确实密文&#xff0c;大致看了下有这几种加密&#xff1a;Md5aes、Md5&a…