首页 > 其他分享 >KDTree求平面最长最短点对

KDTree求平面最长最短点对

时间:2024-11-21 22:29:31浏览次数:1  
标签:arr KDTree max min st 平面 now 最短点 define

更新日志

前言

不会细致讲解KDT内容,如有需要,出门左转KDTree

这篇文章以最常用的二维点集为例(包括模板),其他维度同理。

思路(优化)

我们考虑2-D Tree,维护整个点集。

最朴素的做法是,每次都将当前节点与标准点更新答案,并进入其两个子树计算。不难发现,就是暴搜,没有意义。

引入一个重点:估价函数

这个函数用来估计一个区间与标准点可能的最大值。注意到,这个最大值不一定真实存在,所以称之为估价

更具体的,我们使用整个区间的最大最小坐标与标准点比较。

这个函数的具体作用在 query 部分,在进入一个子树前,先预估这个子树最理想的价值,如果最理想的情况都不比当前 res 更优,就无需进入这个区间了。

另一个优化是优先进入左右子树的顺序。我们看标准点在当前维度上位于中点的左侧或右侧,并以此优先进入可能更优的区间。

比如说获取最短点对,我们就优先进入同侧子树,反之同理。

模板

这个模板求曼哈顿距离下最短点对,其他距离同理。

这个模板内附了动态插入节点的功能(二进制分组)。

#define chmin(a,b) a=min(a,b)
#define chmax(a,b) a=max(a,b)
#define rep(i,x,y) for(int i=x;i<=y;i++)

const int inf=0x3f3f3f3f;
const int N=6e5+5,K=2,T=20;

int n,m;

struct node{
	int v[K];
	int mn[K],mx[K];
	int lson,rson;
}ns[N];
int ak;
bool cmp(int a,int b){return ns[a].v[ak]<ns[b].v[ak];}

inline int dis(int a,int b){
	return abs(ns[a].v[0]-ns[b].v[0])+abs(ns[a].v[1]-ns[b].v[1]);
}
inline int rec(int a,int b){
	int res=0;
	rep(k,0,K-1)res+=max(ns[b].v[k]-ns[a].mx[k],0)+max(ns[a].mn[k]-ns[b].v[k],0);
	return res;
}

struct kdtree{
	int acnt;
	int arr[N];
	int rt[T];
	void update(int x){
		rep(k,0,K-1){
			ns[x].mn[k]=ns[x].mx[k]=ns[x].v[k];
			if(ns[x].lson)chmin(ns[x].mn[k],ns[ns[x].lson].mn[k]),chmax(ns[x].mx[k],ns[ns[x].lson].mx[k]);
			if(ns[x].rson)chmin(ns[x].mn[k],ns[ns[x].rson].mn[k]),chmax(ns[x].mx[k],ns[ns[x].rson].mx[k]);
		}
	}
	int build(int l,int r,int k=0){
		int m=l+r>>1;
		ak=k;nth_element(arr+l,arr+m,arr+r+1,cmp);
		if(l<m)ns[arr[m]].lson=build(l,m-1,k^1);
		if(m<r)ns[arr[m]].rson=build(m+1,r,k^1);
		update(arr[m]);
		return arr[m];
	}
	void append(int &x){
		arr[++acnt]=x;
		if(ns[x].lson)append(ns[x].lson);
		if(ns[x].rson)append(ns[x].rson);
		x=0;
	}
	void insert(int x){
		acnt=0;
		arr[++acnt]=x;
		rep(t,0,T-1){
			if(rt[t])append(rt[t]);
			else{
				rt[t]=build(1,acnt);
				break;
			}
		}
	}
	void insert(int x,int y){
		n++;
		ns[n].v[0]=x;
		ns[n].v[1]=y;
		insert(n);
	}
	int query(int x,int st,int k){
		int res=inf;
		if(x!=st)chmin(res,dis(x,st));
		if(ns[st].v[k]<ns[x].v[k]){
			if(ns[x].lson&&rec(ns[x].lson,st)<res)chmin(res,query(ns[x].lson,st,k^1));
			if(ns[x].rson&&rec(ns[x].rson,st)<res)chmin(res,query(ns[x].rson,st,k^1));
		}else{
			if(ns[x].rson&&rec(ns[x].rson,st)<res)chmin(res,query(ns[x].rson,st,k^1));
			if(ns[x].lson&&rec(ns[x].lson,st)<res)chmin(res,query(ns[x].lson,st,k^1));
		}
		return res;
	}
	int query(int x,int y){
		ns[0].v[0]=x;ns[0].v[1]=y;
		int res=inf;
		rep(t,0,T-1)
			if(rt[t])res=min(res,query(rt[t],0,0));
		return res;
	}
}kdt;

附常用估价函数

统一规定 \(st\) 为标准点,\(now\) 为当前节点。

曼哈顿

最短

大致来说,在某一维度上,如果当前点在区间外,就取其到最近的边界的距离,否则就是 \(0\)。

\[\max(now.x-st.x_{\max},0)+\max(st.x_{\min}-now.x,0)+\max(now.y-st.y_{\max},0)+\max(st.y_{\min}-now.y,0) \]

最长

容易理解,就是到边界的最大距离。

\[\max(|now.x-st.x_{\max}|,|now.x-st.x_{\min})|+\max(|now.y-st.y_{\max}|,|now.y-st.y_{\min}|) \]

欧几里得

前注:考虑精度问题,均不开方。

最短

同理,区间外则到边界,区间内则为 \(0\)。

\[\max(now.x-st.x_{\max},st.x_{\min}-now.x,0)^2+\max(now.y-st.y_{\max},st.y_{\min}-now.y,0)^2 \]

最长

同理,到边界距离最大。

\[\max(now.x-st.x_{\max},now.x-st.x_{\min})^2+\max(now.y-st.y_{\max},now.y-st.y_{\min})^2 \]

例题

LG4169

代码

前注:非题解,不做详细讲解

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

#define chmin(a,b) a=min(a,b)
#define chmax(a,b) a=max(a,b)
#define rep(i,x,y) for(int i=x;i<=y;i++)

const int inf=0x3f3f3f3f;
const int N=6e5+5,K=2,T=20;

int n,m;

struct node{
	int v[K];
	int mn[K],mx[K];
	int lson,rson;
}ns[N];
int ak;
bool cmp(int a,int b){return ns[a].v[ak]<ns[b].v[ak];}

inline int dis(int a,int b){
	return abs(ns[a].v[0]-ns[b].v[0])+abs(ns[a].v[1]-ns[b].v[1]);
}
inline int rec(int a,int b){
	int res=0;
	rep(k,0,K-1)res+=max(ns[b].v[k]-ns[a].mx[k],0)+max(ns[a].mn[k]-ns[b].v[k],0);
	return res;
}

struct kdtree{
	int acnt;
	int arr[N];
	int rt[T];
	void update(int x){
		rep(k,0,K-1){
			ns[x].mn[k]=ns[x].mx[k]=ns[x].v[k];
			if(ns[x].lson)chmin(ns[x].mn[k],ns[ns[x].lson].mn[k]),chmax(ns[x].mx[k],ns[ns[x].lson].mx[k]);
			if(ns[x].rson)chmin(ns[x].mn[k],ns[ns[x].rson].mn[k]),chmax(ns[x].mx[k],ns[ns[x].rson].mx[k]);
		}
	}
	int build(int l,int r,int k=0){
		int m=l+r>>1;
		ak=k;nth_element(arr+l,arr+m,arr+r+1,cmp);
		if(l<m)ns[arr[m]].lson=build(l,m-1,k^1);
		if(m<r)ns[arr[m]].rson=build(m+1,r,k^1);
		update(arr[m]);
		return arr[m];
	}
	void append(int &x){
		arr[++acnt]=x;
		if(ns[x].lson)append(ns[x].lson);
		if(ns[x].rson)append(ns[x].rson);
		x=0;
	}
	void insert(int x){
		acnt=0;
		arr[++acnt]=x;
		rep(t,0,T-1){
			if(rt[t])append(rt[t]);
			else{
				rt[t]=build(1,acnt);
				break;
			}
		}
	}
	void insert(int x,int y){
		n++;
		ns[n].v[0]=x;
		ns[n].v[1]=y;
		insert(n);
	}
	int query(int x,int st,int k){
		int res=inf;
		if(x!=st)chmin(res,dis(x,st));
		if(ns[st].v[k]<ns[x].v[k]){
			if(ns[x].lson&&rec(ns[x].lson,st)<res)chmin(res,query(ns[x].lson,st,k^1));
			if(ns[x].rson&&rec(ns[x].rson,st)<res)chmin(res,query(ns[x].rson,st,k^1));
		}else{
			if(ns[x].rson&&rec(ns[x].rson,st)<res)chmin(res,query(ns[x].rson,st,k^1));
			if(ns[x].lson&&rec(ns[x].lson,st)<res)chmin(res,query(ns[x].lson,st,k^1));
		}
		return res;
	}
	int query(int x,int y){
		ns[0].v[0]=x;ns[0].v[1]=y;
		int res=inf;
		rep(t,0,T-1)
			if(rt[t])res=min(res,query(rt[t],0,0));
		return res;
	}
}kdt;

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>n>>m;
	rep(i,1,n){
		cin>>ns[i].v[0]>>ns[i].v[1];
		kdt.insert(i);
	}
	int t,x,y;
	while(m--){
		cin>>t>>x>>y;
		if(t==1)kdt.insert(x,y);
		if(t==2)cout<<kdt.query(x,y)<<"\n";
	}
	return 0;
}

标签:arr,KDTree,max,min,st,平面,now,最短点,define
From: https://www.cnblogs.com/HarlemBlog/p/18561713

相关文章

  • 深入浅出学算法031-平面分割
    题目描述同一平面内有n(n≤500)条直线,已知其中p(p≥2)条直线相交于同一点,则这n条直线最多能将平面分割成多少个不同的区域?输入两个整数n(n≤500)和p(如果n>=2则2≤p≤n)。输出一个正整数,代表最多分割成的区域数目。样例输入125样例输出73代码走起代码#include<bits/std......
  • KDTree求平面最近点对
    更新日志思路对于每一个点都求一边其最短距离,最后统计。详细地,对于每个区间,先与其中点判断并更新距离(注意特判不能是同一点),然后找出在这一节点排序维度上,查询点到中点距离,记作\(D\)。看查询点在中点左侧/右侧,判断去左右区间。(在这一节点排序的维度上。)同侧更新完之后,如果......
  • 8.4 求微分方程组的数值解 x'=-x^3-y,x(0)=1,y'=x-y^3,y(0)=0.5,0<=t<=30,要求画出x(t)和
    importnumpyasnpimportmatplotlib.pyplotaspltfromscipy.integrateimportsolve_ivpdefsystem(t,state):x,y=statedxdt=-x3-ydydt=x-y3return[dxdt,dydt]t_span=(0,30)y0=[1,0.5]sol=solve_ivp(system,t_span,y0,t_eval=np.linsp......
  • PCL 点云分割 分割多个平面
    目录一、概述1.1原理1.2实现步骤1.3应用场景二、代码实现2.1关键函数2.1.1RANSAC平面分割2.1.2剔除已分割的平面2.1.3可视化点云2.2完整代码三、实现效果PCL点云算法汇总及实战案例汇总的目录地址链接:PCL点云算法与项目实战案例汇总(长期更新)一、概述  ......
  • 点云学习笔记14——PCL点云文件投影到平面
    #include<iostream>#include<pcl/io/pcd_io.h>#include<pcl/point_types.h>#include<pcl/ModelCoefficients.h>#include<pcl/filters/project_inliers.h>#include<pcl/visualization/pcl_visualizer.h>#include<boost/th......
  • OpenGL 和 GLSL 在顶点着色器中动态调整裁剪平面参数的简单代码示例
    以下是一个使用OpenGL和GLSL在顶点着色器中动态调整裁剪平面参数的简单代码示例://OpenGL初始化代码#include<GL/glew.h>#include<GLFW/glfw3.h>#include<iostream>GLFWwindow*window;//初始化GLFWvoidinitGLFW(){if(!glfwInit()){std::cer......
  • 平面点云凹边界提取
    目录1原理介绍        α-shape的基础概念数学公式推导2.1外接圆半径2.2根据α参数筛选三角形2.3构建α-shape2.4参数调整与优化3α-shape的构建步骤4示例代码        取点云的凹边界是计算几何中的一个经典问题。凹边界与凸边界不同,它......
  • 基于SSM平面设计课程在线学习系统的设计
    管理员账户功能包括:系统首页,个人中心,学生管理,教师管理,课程类型管理,课程学习管理,试题讲解管理,作业信息管理前台账号功能包括:系统首页,个人中心,课程学习,在线讨论,系统公告,后台管理开发系统:Windows架构模式:SSMJDK版本:JavaJDK1.8开发工具:IDEA(推荐)数据库版本:mysql5.7数据库......
  • 平面图形中建系 | 平面直角坐标系
    前情概要如果没有笛卡尔平面直角坐标系,那么涉及平面向量的问题只能用基向量的方法[形的角度]求解,不能用代数方法[数的角度]计算;同理如果没有空间直角坐标系的介入,立体几何中的问题也就只能从形的角度思考,而不能用代数方法[数的角度]来计算;所以建系的目的主要是想把有关形的问题,通......
  • manim边学边做--极坐标平面
    PolarPlane,顾名思义,是用于创建极坐标平面的类。与笛卡尔坐标系不同,极坐标系是基于角度和半径来定位点的,这里的每个点由一个角度和距离原点的距离表示。在Manim中,PolarPlane通过极径($r\()和极角(\)\theta$)来展示坐标系,这种表示方式便于处理与角度和半径相关的数学概念。无论是......