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

news/2024/5/20 12:05:16

【题目来源】
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.mrgr.cn/p/82834331

相关文章

Altium PCB添加平衡铜/盗铜的方法(依旧是简单粗暴)

最近画的板子遇到了PCB残铜率不足的问题,一般想法也是用整板覆铜的方法来填满空旷的区域,但是这个会带来很多碎铜,特别是表层有元器件,覆铜会产生更多碎铜,但是不覆铜又会导致残铜率低,板厂的说法是残铜率过低会导致PCB外层电镀时电流不均衡,后果就是铜箔厚度不均匀,内…

记录: 小红书笔记采集接口 获取用户笔记列表

调研发现iDataRiver平台 https://idatariver.com/zh-cn/project/0eab 上有供应商上架小红书公开数据接口API,可获取笔记详情,搜索笔记,用户信息等。为了维护公司在小红书平台上的账号数据以及运营分析,需要用到小红书数据采集相关的公开接口进行辅助管理。 近期调研发现iDa…

2024年软件测试最全渗透测试工具_下载地址1下载地址2下载地址3(1),我了解到的面试的一些小内幕

网上学习资料一大堆&#xff0c;但如果学到的知识不成体系&#xff0c;遇到问题时只是浅尝辄止&#xff0c;不再深入研究&#xff0c;那么很难做到真正的技术提升。 需要这份系统化的资料的朋友&#xff0c;可以戳这里获取 一个人可以走的很快&#xff0c;但一群人才能走的更…

关于Java Chassis 3的契约优先(API First)开发

契约优先(API First)开发是指应用程序开发过程中,将API设计作为第一优先级的任务。本文分享自华为云社区《Java Chassis 3技术解密:契约优先(API First)开发》,作者: liubao68。 契约优先(API First)开发是指应用程序开发过程中,将API设计作为第一优先级的任务。契约…

Pytharm2020安装详细教程

Pytharm2020版提取链接链接&#xff1a; https://pan.baidu.com/s/1eDvwYmUJ4l7kIBXewtN4EA?pwd1111 提取码&#xff1a;1111 演示版本为2019版&#xff0c;链接包为2020版pytharm。 1.双击exe文件页面会提示更改选项&#xff0c;点击“是”。 2.点击下一步next 自…

Metasploit Framework(MSF)从入门到实战(二)

Metasploit Framework&#xff08;MSF&#xff09;从入门到实战&#xff08;一&#xff09;_安装msf更新-CSDN博客 MSF模块介绍 MSF有7个模块&#xff0c;分别对下面目录下的7个子文件夹&#xff1a; auxiliary&#xff08;辅助模块 &#xff09; show auxiliary //查看所有…

02-单片机商业项目编程,从零搭建低功耗系统设计

一、本文内容 上一节《01-单片机商业项目编程&#xff0c;从零搭建低功耗系统设计-CSDN博客》已经对事件驱动原理有个基本了解&#xff0c;本节主要就是如何将事件写的更规范&#xff0c;而不是用t_flag这样的标记&#xff0c;写多了可读性也不强&#xff1b;本节结尾总结将提出…

vmware虚拟机内删除文件后宿主机空间不释放

问题描述 linux下&#xff0c;vmware内虚拟机删除文件&#xff0c;宿主机空间不释放&#xff0c;D盘快满了 解决方法 通过vmware-toolbox进行空间回收 安装 在虚拟机内操作 yum install -y open-vm-tools 清理 在虚拟机内操作 #查看磁盘的挂载点 sudo /usr/bin/vmware…

Java | Leetcode Java题解之第77题组合

题目&#xff1a; 题解&#xff1a; class Solution {List<Integer> temp new ArrayList<Integer>();List<List<Integer>> ans new ArrayList<List<Integer>>();public List<List<Integer>> combine(int n, int k) {List&l…

分享一个php常驻内存多进程任务的扩展

前言 最近在摸鱼的时候发现一个PHP常驻内存多进程任务扩展包&#xff1a;EasyTask: PHP常驻内存多进程任务管理器&#xff0c;支持定时任务(PHP resident memory multi-process task manager, supports timing tasks) (gitee.com)&#xff0c;支持php使用多线程处理任务。之前…

【使用ChatGPT的API之前】OpenAI API提供的可用模型

文章目录 一. ChatGPT基本概念二. OpenAI API提供的可用模型1. InstructGPT2. ChatGPT3. GPT-4 三. 在OpenAI Playground中使用GPT模型-ing 在使用GPT-4和ChatGPT的API集成到Python应用程序之前&#xff0c;我们先了解ChatGPT的基本概念&#xff0c;与OpenAI API提供的可用模型…

dotnet 9 WPF 支持 Style 的 Setter 填充内容时可忽略 Value 标签

本文记录 WPF 在 dotnet 9 的一项 XAML 编写语法改进点,此改进点用于解决编写 Style 的 Setter 进行给 Value 赋值时,不能将 Value 当成默认内容,需要多写 Value 标签的问题。通过此改进点可减少两行 XAML 代码在原先的 WPF 版本里面,对 Style 的 Setter 填充复杂的对象内容…

Java 中的 HTTP 客户端库OkHttp、Apache HttpClient和HttpUrlConnection

大家好&#xff0c;我是G探险者。 项目开发里面经常会有这么一种场景&#xff1a;与服务器进行 HTTP 通信。一般存在于服务间远程调用的场景 Java 生态系统提供了多种 HTTP 客户端库&#xff0c;每种都有其自己的特点、优势和适用场景。 本文将介绍几种主要的 Java HTTP 客户…

Cheetah3D for Mac - 轻松打造专业级3D作品

对于追求专业级3D作品的设计师来说&#xff0c;Cheetah3D for Mac无疑是一款不可多得的工具。 这款软件拥有强大的建模、渲染和动画功能&#xff0c;能够满足您在3D设计方面的各种需求。通过简单的操作&#xff0c;您可以轻松构建出复杂的3D模型&#xff0c;并为其添加逼真的材…

树莓派4b红外检测

1.红外检测连接图 2.红外检测工作原理 红外传感器的工作原理类似于物体检测传感器。该传感器包括一个红外LED和一个红外光电二极管&#xff0c;因此通过将这两者结合起来&#xff0c;可以形成一个光耦合器。 红外LED是一种发射红外辐射的发射器。该LED看起来与标准LED相似&a…

Apache DolphinScheduler 4月简报:社区发展与技术革新速递

各位热爱 DolphinScheduler 的小伙伴们&#xff0c;4 月份的 DolphinScheduler 社区月报更新啦&#xff01;这里将记录 DolphinScheduler 社区每月的重要更新&#xff0c;欢迎关注&#xff01; 月度 Merge 之星 感谢以下小伙伴 4 月为 Apache DolphinScheduler 所做的精彩贡献…

Jmeter用jdbc实现对数据库的操作

我们在用Jmeter进行数据库的操作时需要用到配置组件“JDBC Connection Configuration”&#xff0c;通过配置相应的驱动能够让我们通过Jmeter实现对数据库的增删改查&#xff0c;这里我用的mysql数据库一起来看下是怎么实现的吧。 1.驱动包安装 在安装驱动之前我们要先查看当前…

5月记录

76.CF1967 Codeforces Round 942 (Div. 1) CF1967A CF1967B1 \[b\times \gcd(a,b)|a+b \to qi^2|(p+q)i \to qi|(p+q)\to q|p \to b|a \]反过来也能推到。 CF1967B2 \[a+b|b\times \gcd(a,b) \to (p+q)i|qi^2\to (p+q)|qi \to (p+q)|i \]枚举 \(p,q\),因为 \(p<i,pi< n\…

ssm111基于MVC的舞蹈网站的设计与实现+vue

基于MVC的舞蹈网站的设计与实现vue 摘 要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;舞蹈网站当然也不能排除在外。舞蹈网站是以实际运用为开发背景&#xff0c;运用软件…

标准引领 | 竹云参编《面向云计算的零信任体系》行业标准正式发布!

近日&#xff0c;中华人民共和国工业和信息化部公告2024年第4号文件正式发布行业标准&#xff1a;YD/T 4598.1-2024《面向云计算的零信任体系 第1部分&#xff1a;总体架构》&#xff08;后简称“总体架构”&#xff09;&#xff0c;并于2024年7月1日起正式实施。 该标准汇集大…