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

【题目来源】
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 readint 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
3out:
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.mzph.cn/bicheng/10643.shtml

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

相关文章

基于微信小程序的图书馆预约系统的设计与实现

个人介绍 hello hello~ &#xff0c;这里是 code袁~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f981;作者简介&#xff1a;一名喜欢分享和记录学习的…

linux系统(ubuntu)调用科大讯飞SDK实现语音识别

1. 科大讯飞官网 登录注册实名制 2. 点击控制台&#xff0c;创建应用 点击左侧的语音听写&#xff0c;右边下滑选择Linux&#xff0c;点击下载 选择Linux平台&#xff0c;普通版本&#xff0c;语音听写&#xff0c;SDK下载 此时将得到一个压缩包&#xff0c;选择的功能不…

集成平台建设方案(大数据中台技术方案)—Word原件

基础支撑平台主要承担系统总体架构与各个应用子系统的交互&#xff0c;第三方系统与总体架构的交互。需要满足内部业务在该平台的基础上&#xff0c;实现平台对于子系统的可扩展性。基于以上分析对基础支撑平台&#xff0c;提出了以下要求&#xff1a; 基于平台的基础架构&…

2024自动化测试市场分析

大家都说2024年软件测试讲会卷的更厉害,从原来的功能测试到现在自动化测试,那么2024年是否可以学习自动化冲一把,我们先看一下2023年自动化测试在测试行业中的分析: 1.市场需求增长&#xff1a; 随着技术的进步和企业对软件质量的要求日益提高&#xff0c;自动化测试在测试行…

手把手微调大模型【附:一镜到底视频教程】

前言 近期有很多小伙伴来问是否有大模型微调教程&#xff0c;其实目前网上有很多教程&#xff0c;但是据了解&#xff0c;由于网上教程质量参差不齐&#xff0c;导致很多小伙伴尤其是初学者&#xff0c;一坑未出又入一坑&#xff0c;有种从入门到放弃的感觉。于是乎&#xff0…

Linux提权--内核漏洞--web用户提权(脏牛)本地提权(脏管道)

免责声明:本文仅做技术交流与学习... 目录 Linux-内核漏洞Web用户提权-探针&利用-脏牛dcow nmap扫描目标IP及端口 导入脚本,进行探针 通过MSF发现目标机器存在脏牛漏洞 ---上传信息搜集的文件,查找漏洞,利用漏洞,继续上传EXP. --密码改了,再用xshell连一下就行了. …

VMware VCP考试快速通过的方法

VMware VCP-DVC 2024年5月10号刚有同事考过的&#xff0c;可以一条龙 &#xff0c;非常省心&#xff0c;最重要的是稳 总结 通过VCP(VMware Certified Professional)认证&#xff0c;需要参加培训机构的的培训&#xff08;没有时间&#xff1f;当然也可以不&#xff09;&…

[嵌入式系统-75]:RT-Thread-快速上手:正点原子探索者 STM32F407示例

目录 正点原子探索者 STM32F407 上手指南 1. 简介 2. 准备工作 3. 运行第一个示例程序 3.1 编译下载 3.2 运行 继续学习 正点原子探索者 STM32F407 上手指南 1. 简介 探索者 STM32F407 是正点原子推出的一款基于 ARM Cortex-M4 内核的开发板&#xff0c;最高主频为 16…

关键点检测——面部情绪数据集

引言 亲爱的读者们&#xff0c;您是否在寻找某个特定的数据集&#xff0c;用于研究或项目实践&#xff1f;欢迎您在评论区留言&#xff0c;或者通过公众号私信告诉我&#xff0c;您想要的数据集的类型主题。小编会竭尽全力为您寻找&#xff0c;并在找到后第一时间与您分享。 …

算法提高之字串变换

算法提高之字串变换 核心思想&#xff1a;双向广搜 双向bfs 建立两个队列 一起bfs到中间态 #include <iostream>#include <cstring>#include <algorithm>#include <queue>#include <unordered_map>using namespace std;const int N 6;int n;…

【北京迅为】《iTOP-3588从零搭建ubuntu环境手册》-第6章 安装Samba

RK3588是一款低功耗、高性能的处理器&#xff0c;适用于基于arm的PC和Edge计算设备、个人移动互联网设备等数字多媒体应用&#xff0c;RK3588支持8K视频编解码&#xff0c;内置GPU可以完全兼容OpenGLES 1.1、2.0和3.2。RK3588引入了新一代完全基于硬件的最大4800万像素ISP&…

【数据结构】顺序表(一)

✨✨✨专栏&#xff1a;数据结构 &#x1f9d1;‍&#x1f393;个人主页&#xff1a;SWsunlight 不怕别人看不起&#xff0c;就怕自己不争气。路是人走出来的&#xff0c;关键要靠自己闯。振作起来&#xff0c;生活的含义就是前进。 目录 一、顺序表的概念&#xff1a; 二…

SQLite性能测试(插入)

最近一直在思考一个问题&#xff0c;SQLite 做到这么轻量级&#xff0c;那它注定不会像 MySql 一样强性能&#xff0c;那么它的性能怎么样呢&#xff1f;并发量多高呢&#xff1f; 官方解释&#xff1a; About SQLite 最大数据库大小&#xff1a;281TB 最大行大小&#xff1…

音视频--AAC编码解析和示例

目录 1&#xff1a;AAC编码介绍 2&#xff1a;AAC格式介绍 3&#xff1a;AAC -ADTS帧组成 4&#xff1a;AAC-ADTS&#xff1a;&#xff08;adts_fixed_header&#xff09;格式介绍 5&#xff1a;AAC-ADTS&#xff1a;&#xff08;adts_variable_header&#xff09;格式介绍…

C语言收尾 预处理相关知识

一. 预处理详解 1.1 预定义符号 FILE //进行编译的源文件LINE //文件当前的行号DATE //文件被编译的日期TIME //文件被编译的时间FUNCTION //文件当前所在的函数STDC //如果编译器遵循ANSI C标准&#xff0c;其值为1&#xff0c;否则未定义 这些预定义符号都是语言内置的 我们…

类和对象中篇

类的六个默认成员函数 如果一个类中什么成员都没有&#xff0c;简称为空类。空类中什么都没有吗&#xff1f;并不是的&#xff0c;任何一个类在我们不写的情况下&#xff0c;都会自动生成下面6个默认成员函数 ①初始化和清理&#xff1a;构造函数和析构函数 ②拷贝复制&#x…

Linux——信号量

什么是信号量&#xff1f; 信号量是用来用于同步和互斥的。其实就是一个计数器&#xff0c;进行PV操作&#xff0c;其中P操作就是让计数器–&#xff0c;V操作就是让计数器。 从物理上说明信号量的P、V操作的含义。 P(S)表示申请一个资源&#xff0c;S.value>0表示有资源可…

OBS插件--复合模糊

复合模糊 复合是一款滤镜插件&#xff0c;支持多种模糊类型和多种蒙版效果。支持模糊源的部分显示区域&#xff0c;可以反选区域进行模糊&#xff0c;这个功能对于场景部分区域需要遮盖非常实用。 下面截图演示下操作步骤&#xff1a; 首先&#xff0c;打开 OBS直播助手 在…

【Unity 鼠标输入检测】

Unity 鼠标输入检测 Unity提供了多种方法来检测和处理鼠标输入&#xff0c;允许开发者在游戏中实现对鼠标移动、点击和滚轮滚动的响应。以下是一些基本的鼠标输入检测方法&#xff1a; 1. Input.mousePosition 这个属性返回当前鼠标指针的屏幕坐标。坐标是以像素为单位的&…

【吊打面试官系列】Java高并发篇 - 同步方法和同步块,哪个是更好的选择?

大家好&#xff0c;我是锋哥。今天分享关于 【同步方法和同步块&#xff0c;哪个是更好的选择&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; 同步方法和同步块&#xff0c;哪个是更好的选择&#xff1f; 同步块是更好的选择&#xff0c;因为它不会锁住整个对象…