K-D Tree学习笔记

引入

K-D Tree 是一种处理高维空间的数据结构。

支持O(nk−1k)O(n^{\frac {k-1}k})O(nkk1)查询给定超矩形内的点的信息, kkk 为维数。

可以用替罪羊树的思想实现动态插入。

但其实用的最多的是在错误的复杂度内查询奇奇怪怪的点信息。

构建

K-D Tree 是平衡树的结构(但应该不能叫平衡树)。

它的核心思想是通过树一层层的分化,把整个空间分割成若干子空间。

但是点是kkk维的,而树只有二维,所以一次我们只能选择一维来划分。

我们关心的问题是如何选取进行比较的维度。

一种方法是选取方差最大的维度,但在OI中不常用

OI中一般轮流选取,即深度为ddd的结点按第d%kd\%kd%k维的坐标划分

具体而言,设点的序列为ppp,对于一个需要构建区间 [L,R][L,R][L,R],设当前需要分的维度为ddd

如果 L=RL=RL=R ,那么划分出的只有当前一个点

为了保证尽量均匀,我们选取这一维的中位数作为当前子树的根

mid=(l+r)/2

可以使用 C++ STL 中一个奥妙重重的函数 nth_element

它可以把第 kkk 小的元素放在第 kkk 位,然后比它小的放在左边(但并不保证有序,后同),比它大的放在右边。复杂度为 O(n)O(n)O(n) (其实就是阉割版的快排)

这样我们 nth_element(p+l,p+mid,p+r+1) 就完事了

因为已经帮你分好了 ,所以把中位数间个点当根,然后递归 [L,mid−1][L,mid-1][L,mid1][mid+1,R][mid+1,R][mid+1,R]

最后更新子树信息,我们要维护的是超矩形,记录一下每一维的最小值和最大值就可以了。

复杂度 O(nlog⁡n)O(n\log n)O(nlogn)

插入

用平衡树的方法插入,根据当前层数判断往左还是往右

当然可能会插成链,你又不能旋转,所以只能考虑重构

暴力推平重新建就可以了,没啥技术含量

只是重建带一个 log⁡\loglog ,所以可能跑得有点慢……

询问

询问一个 kkk 维超矩形内所有点的信息

用线段树的思想无脑递归就可以了

如果完全包含当前点划分出的子空间直接返回当前子树的值,没有相交返回 000

复杂度可以证明是O(n1−1k)O(n^{1-\frac 1k})O(n1k1)

对于 k=2k=2k=2 的情况提供一个不知道对不对的证明:

对于一个结点,设其子树大小为nnn,我们考虑往下分两层

在这里插入图片描述
T1(n)T_1(n)T1(n)表示询问中间挖一块的复杂度

在这里插入图片描述
T2(n)T_2(n)T2(n)表示挖一个角的复杂度

在这里插入图片描述
T3(n)T_3(n)T3(n)为平行挖的复杂度

在这里插入图片描述
显然有

T1(n)=4T2(n4)T2(n)=2T3(n4)+T2(n4)T3(n)=2T3(n4)T_1(n)=4T_2(\frac n4)\\T_2(n)=2T_3(\frac n4)+T_2(\frac n4)\\T_3(n)=2T_3(\frac n4)T1(n)=4T2(4n)T2(n)=2T3(4n)+T2(4n)T3(n)=2T3(4n)

解得

T1(n)=T2(n)=T3(n)=nT_1(n)=T_2(n)=T_3(n)=\sqrt nT1(n)=T2(n)=T3(n)=n

有一些变种,比如一次函数的下方、最近点对,但这些复杂度都是错的。

不过如果出题人不刻意卡跑得还是挺快的,是个骗分的不错选择(

例题

Luogu4148 简单题

有一个n×nn\times nn×n的方阵,开始时值都为000,维护mmm次操作:

  1. (x,y)(x,y)(x,y)的位置加上vvv
  2. 询问一个矩阵内的数字和

n≤5×105,m≤2×105n\leq5\times10^5,m\leq2\times10^5n5×105,m2×105

强制在线,空间限制20M

模板题

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#define MAXN 200005
using namespace std;
inline int read()
{int ans=0;char c=getchar();while (!isdigit(c)) c=getchar();while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();return ans;
}
const double alpha=0.7;
struct point{int x[2];point(const int& a=0,const int& b=0){x[0]=a,x[1]=b;}};
inline point min(const point& a,const point& b){return point(min(a.x[0],b.x[0]),min(a.x[1],b.x[1]));}
inline point max(const point& a,const point& b){return point(max(a.x[0],b.x[0]),max(a.x[1],b.x[1]));}
inline bool operator ==(const point& a,const point& b){return a.x[0]==b.x[0]&&a.x[1]==b.x[1];}
int val[MAXN],sum[MAXN],siz[MAXN],ch[MAXN][2],tot;
point cur[MAXN],L[MAXN],R[MAXN];
int rt,*tag,dir;
int rub[MAXN];
inline int newnode(const int& v,const point& p)
{int x=rub[0]? rub[rub[0]--]:++tot;val[x]=sum[x]=v,siz[x]=1,cur[x]=L[x]=R[x]=p;ch[x][0]=ch[x][1]=0;return x;
}
inline void update(const int& x)
{sum[x]=val[x]+sum[ch[x][0]]+sum[ch[x][1]];siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;L[x]=min(cur[x],min(L[ch[x][0]],L[ch[x][1]]));R[x]=max(cur[x],max(R[ch[x][0]],R[ch[x][1]]));
}
void insert(int& x,int k,int v,const point& p)
{if (!x) return (void)(x=newnode(v,p));if (cur[x]==p) return val[x]+=v,update(x);int d=p.x[k]<cur[x].x[k];insert(ch[x][d],k^1,v,p);update(x);if (siz[ch[x][d]]>siz[x]*alpha) tag=&x,dir=k;
}
struct node{int v;point p;};
node buf[MAXN];
int cnt;
int cur_k;
inline bool operator <(const node& a,const node& b){return a.p.x[cur_k]<b.p.x[cur_k];}
void build(int& x,int k,int l,int r)
{if (l>r) return (void)(x=0);int mid=(l+r)>>1;cur_k=k,nth_element(buf+l,buf+mid,buf+r+1);x=newnode(buf[mid].v,buf[mid].p);build(ch[x][0],k^1,l,mid-1),build(ch[x][1],k^1,mid+1,r);update(x);
}
void dfs(int x)
{if (!x) return;dfs(ch[x][0]);buf[++cnt].p=cur[x],buf[cnt].v=val[x],rub[++rub[0]]=x;dfs(ch[x][1]);
}
inline void rebuild()
{cnt=0;dfs(*tag);build(*tag,dir,1,cnt);
}
int query(int x,point l,point r)
{if (l.x[0]<=L[x].x[0]&&R[x].x[0]<=r.x[0]&&l.x[1]<=L[x].x[1]&&R[x].x[1]<=r.x[1]) return sum[x];if (r.x[0]<L[x].x[0]||R[x].x[0]<l.x[0]||r.x[1]<L[x].x[1]||R[x].x[1]<l.x[1]) return 0;int ans=0;if (l.x[0]<=cur[x].x[0]&&cur[x].x[0]<=r.x[0]&&l.x[1]<=cur[x].x[1]&&cur[x].x[1]<=r.x[1]) ans=val[x];return ans+query(ch[x][0],l,r)+query(ch[x][1],l,r);
}
int main()
{memset(L,0x7f,sizeof(L));read();int lans=0;while (true){int x1,y1,x2,y2,v;switch(read()){case 1:x1=read()^lans,y1=read()^lans,v=read()^lans;tag=NULL;insert(rt,0,v,point(x1,y1));if (tag!=NULL) rebuild();break;case 2:x1=read()^lans,y1=read()^lans,x2=read()^lans,y2=read()^lans;printf("%d\n",lans=query(rt,point(x1,y1),point(x2,y2)));break;case 3:return 0;}}
} 

Luogu4475 巧克力王国

题意:给定nnnxi,yix_i,y_ixi,yi,mmm次询问,每次给定a,b,ca,b,ca,b,c,求ax+by<cax+by<cax+by<c(x,y)(x,y)(x,y)的个数

n,m≤5×104,−109≤x,y,a,b,c≤109n,m\leq 5\times 10^4,-10^9\leq x,y,a,b,c\leq10^9n,m5×104,109x,y,a,b,c109

先口胡一份

显然满足条件的(x,y)(x,y)(x,y)在一个一次函数下方

建出K-D Tree后,询问时因为是矩形,如果四个点都在这个函数下方,那么整个矩形中的点都在下方,直接返回

注意这个复杂度是错误的,只是数据没卡跑得快

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

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

相关文章

Codeforces Round #633 B. Edge Weight Assignment 结论题 + dp

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 首先考虑最小值&#xff0c;如果从一个叶子结点出发到任意叶子的距离都为偶数&#xff0c;那么只需要一个值就可以满足条件。如果有奇数的&#xff0c;考虑111 ^ 222 ^ 303030&#xff0c;…

ASP.NET Core Web API中使用Swagger

本节导航Swagger介绍在ASP.NET CORE 中的使用swagger在软件开发中,管理和测试API是一件重要而富有挑战性的工作。在我之前的文章《研发团队,请管好你的API文档》 也专门阐述了通过文档管理工具,来保证API文档和代码的一致性,这样更加有助于团队的协作。以往我们总是通过第三方平…

【ZJOI2015】幻想乡战略游戏【点分树】【带权重心】

题意&#xff1a;nnn个点带边权的树&#xff0c;动态修改点权viv_ivi​&#xff0c;最小化 钦定一个点xxx 后 ∑idist(x,i)∗vi\sum\limits_{i} dist(x,i)*v_ii∑​dist(x,i)∗vi​的值。 n,q≤105n,q \leq10^5n,q≤105&#xff0c;度数不超过202020 限制度数的树上的一些诡异…

NuGet是什么?理解与使用

如果你了解python&#xff0c;那么它类似pip。如果你了解nodejs&#xff0c;那么它类似npm。如果你了解ruby&#xff0c;那么它类似gem。对&#xff0c;它就是一个包&#xff08;package&#xff09;管理平台&#xff0c;确切的说是 .net平台的包管理工具&#xff0c;它提供了一…

P2468 [SDOI2010]粟粟的书架 主席树 + 二分 + 二维前缀和

传送门 文章目录题意&#xff1a;题意&#xff1a;题意&#xff1a; 给你一个n∗mn*mn∗m的矩阵&#xff0c;每次询问一个矩形&#xff0c;左上角是(x1,y1)(x_1,y_1)(x1​,y1​)&#xff0c;右上角是(x2,y2)(x_2,y_2)(x2​,y2​)&#xff0c;和一个hhh&#xff0c;问是否能选出…

带花树学习笔记

引入 带花树解决的是一般图的最大匹配问题。 学习此算法前你需要掌握二分图匹配的匈牙利算法&#xff0c;至少需要知道它的思想。 理论 众所周知&#xff0c;二分图匹配的思想是不断地找增广路。 严谨地讲&#xff0c;是找到了一条简单路径&#xff0c;它的起点和终点都未…

你竟然没用 Nuget 构建项目?

想写这篇文章缘起于公众号文章里的一条留言&#xff1a;对于一个现代化的开发平台&#xff0c;建立一种让开发者创建&#xff0c;分享与使用可复用代码的机制是十分必要的。这种“可复用代码”被打包后的文件通常被称作“包”&#xff08;package&#xff09;&#xff0c;对于.…

Codeforces Round #339 (Div. 1) D. Kingdom and its Cities 虚树 + dp

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; n≤1e5,∑k≤1e5,q≤1e5n\le1e5,\sum k\le1e5,q\le1e5n≤1e5,∑k≤1e5,q≤1e5。 思路&#xff1a; 经过分析&#xff0c;外敌占领的城市只有可能是两点的lcalcalca或者两点之间任意一点&#xff0c;是哪个点…

小白开学Asp.Net Core《四》 —— 使用AspectCore-Framework

小白开学Asp.Net Core《四》—— 使用AspectCore-Framework一、AspectCore-Framework说AspectCore-Framework不得不先谈谈的AOP&#xff0c;AOP&#xff1a;在软件业&#xff0c;AOP为Aspect Oriented Programming的缩写&#xff0c;意为&#xff1a;面向切面编程&#xff0c;通…

P4103 [HEOI2014]大工程 虚树 + dp

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 简化一下题意就是求树上给定点集中每两个点之间的距离之和&#xff0c;相距最远的点&#xff0c;相距最近的点。 对于距离和我们统计一下边的贡献就好啦&#xff0c;边两头的sizesizesize乘…

【翻译】Keras.NET简介 - 高级神经网络API in C#

用C#&#xff0c;远离996Keras.NETKeras.NET是一个高级神经网络API&#xff0c;它使用C#编写&#xff0c;并带有Python绑定&#xff0c;可以在Tensorflow、CNTK或Theano上运行。其关注点是实现快速实验。因为做好研究的关键是&#xff1a;能在尽可能短的时间内从一个想法发展出…

TIOBE 6月排行:C# 以微弱的优势超过了 Visual Basic .NET 的排名,再次进入 TOP 5

月经贴警告……TIOBE 编程语言排行榜 7 月更新已公布&#xff0c;排名前十的分别是&#xff1a;Java, C, Python, C, C#, Visual Basic .NET, JavaScript, PHP, SQL 和汇编语言。和上个月的不同之处主要是 C# 以微弱的优势超过了 Visual Basic .NET 的排名&#xff0c;再次进入…

用.NET Core实现一个类似于饿了吗的简易拆红包功能

需求说明以前很讨厌点外卖的我&#xff0c;最近中午经常点外卖&#xff0c;因为确实很方便&#xff0c;提前点好餐&#xff0c;算准时间&#xff0c;就可以在下班的时候吃上饭&#xff0c;然后省下的那些时间就可以在中午的时候多休息一下了。点餐结束后&#xff0c;会有一个好…

kubernetes实战篇之helm示例yaml文件文件详细介绍

前面完整示例里,我们主要讲解helm打包,部署,升级,回退等功能,关于这里面的文件只是简单介绍,这一节我们详细介绍一下这里面的文件,以方便我们参照创建自己的helm chart.Helm Chart 结构Chart 目录结构mychart/ Chart.yaml LICENSE README.md values.yaml requirements.yam…

P4309 [TJOI2013]最长上升子序列 平衡树 + dp

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 注意到一个很关键的条件&#xff0c;每次插入iii&#xff0c;而iii是递增的&#xff0c;也就是说插入iii之后只会从前面的最大值转移过来&#xff0c;所以我们现在只需要维护插入操作即可&…

【AGC035C】Skolem XOR Tree【异或】【构造】

传送门 题意&#xff1a;给定nnn&#xff0c;构造或判断无法构造一个2n2n2n个结点的树&#xff0c;其中结点iii和ininin的权值为iii,且所有iii和ininin路径权值异或和等于iii。 注意到 2i⊕2i112i\oplus2i112i⊕2i11&#xff0c;然后可以脑补出 然而111没处理 发现1⊕2⊕301\…

ASP.NET Core 管道再探

几乎任何服务器端处理环境都有自己的直通组件管道&#xff0c;用于检查、重路由或修改传入请求和传出响应。经典 ASP.NET 围绕 HTTP 模块理念进行排列&#xff0c;而 ASP.NET Core 采用基于中间件组件的更现代的体系结构。最终目的是相同的 - 允许可配置的外部模块以请求&#…

GRPC与.net core

QQ讨论群&#xff1a;953553560正文系列章节GRPC与.net coreGRPC截止时间与元数据GRPC与netcore IdentityGRPC与netcore IdentityServer4概述GRPC的数据交互模式有&#xff1a;1.单项RPC&#xff0c;最简单的数据交换方式&#xff0c;客户端发出单个请求&#xff0c;收到单个响…

程序员修神之路--做好分库分表其实很难之一

点击上方“蓝字”带你去看小星星菜哥&#xff0c;领导让我开发新系统了这么说领导对你还是挺信任的呀~必须的&#xff0c;为了设计好这个新系统&#xff0c;数据库设计我花了好多心思呢做一个系统我觉得不应该从数据库入手&#xff0c;应该从设计业务模型开始&#xff0c;先不说…

Office 365开发者的前端必备课程

这篇文章其实不仅仅是写给Office 365开发者的&#xff0c;但对于广大的Office 365开发者来说确实相当重要的。这里提到的Office 365开发者&#xff0c;包括了Office Add-ins&#xff0c;SharePoint Add-ins&#xff0c;Microsoft Graph&#xff0c;Microsoft Teams的开发者。我…