KD-Tree,用来解决多维空间中的问题,其实就是优化暴力(逃

一般cdq能做的它都能做,而且。。。既然是优化暴力,那就学习一下了

对与几个n维点,我们将它每一维分割,建立一颗二叉树,方便我们搜索剪枝

它好像插入比较麻烦,和替罪羊一样暴力重构,博主蒟蒻不会啦

KD-Tree能解决的问题:平面上点对最小,最大距离,k大距离(包括曼哈顿距离和欧式距离)

当然这是我的理解,可能会有偏差

KD-Tree基本模板:

struct node{
int d[2],l,r,mx[2],mn[2],id;
friend bool operator < (node a,node b){
return a.d[now]<b.d[now];
}
}ask,tr[MAXN];
struct KD_TREE{
void update(int x){
int l=tr[x].l,r=tr[x].r;
for(int i=0;i<=1;i++){
tr[x].mn[i]=tr[x].mx[i]=tr[x].d[i];
if(l!=0){
tr[x].mn[i]=min(tr[x].mn[i],tr[l].mn[i]);
tr[x].mx[i]=max(tr[x].mx[i],tr[l].mx[i]);
}
if(r!=0){
tr[x].mn[i]=min(tr[x].mn[i],tr[r].mn[i]);
tr[x].mx[i]=max(tr[x].mx[i],tr[r].mx[i]);
}
}
}
int dis(node a,node b){
int res=0;
for(int i=0;i<=1;i++){
res+=power(a.d[i]-b.d[i]);
}
return res;
}
int get_dis(node a){
int res=0;
for(int i=0;i<=1;i++){
res+=max(power(a.mx[i]-ask.d[i]),power(a.mn[i]-ask.d[i]));
}
return res;
}
void build(int &rt,int l,int r,int d){
int mid=(l+r)>>1;
now=d;
nth_element(tr+l,tr+mid,tr+r+1);
if(l<mid) build(tr[mid].l,l,mid-1,d^1);
if(r>mid) build(tr[mid].r,mid+1,r,d^1);
update(mid);
rt=mid;
}
void query(int x){
if(!x) return ;
int sum_l=inf,sum_r=inf,dist=dis(tr[x],ask);
if(tr[x].l) sum_l=get_dis(tr[tr[x].l]);
if(tr[x].r) sum_r=get_dis(tr[tr[x].r]);
if(dist>-q.top()){
q.pop();
q.push(-dist);
}
if(sum_l>sum_r){
if(sum_l>=-q.top()) query(tr[x].l);
if(sum_r>=-q.top()) query(tr[x].r);
}else{
if(sum_r>=-q.top()) query(tr[x].r);
if(sum_l>=-q.top()) query(tr[x].l);
}
}
}KD_Tree;

其中dis和get_dis是随题而定,其他的基本不变

例题:Hide and Seek

题目大意:给出平面内几个点的坐标,求曼哈顿距离最小值

挺水的,上代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 500005
#define inf 0x7fffffff
using namespace std;
int read(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9'){ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x;
}
int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a<b?a:b;}
int abs(int a){return a<0?-a:a;}
int n,now,root,ans=inf;
struct node{
int d[2],l,r,mx[2],mn[2];
friend bool operator < (node a,node b){
return a.d[now]<b.d[now];
}
friend int dis(node a,node b){
return abs(a.d[0]-b.d[0])+abs(a.d[1]-b.d[1]);
}
}tr[MAXN];
struct KD_TREE{
node p[MAXN],t;
int ans;
private:
void update(int x){
int l=p[x].l,r=p[x].r;
for(int i=0;i<=1;i++){
if(l!=0){
p[x].mn[i]=min(p[x].mn[i],p[l].mn[i]);
p[x].mx[i]=max(p[x].mx[i],p[l].mx[i]);
}
if(r!=0){
p[x].mn[i]=min(p[x].mn[i],p[r].mn[i]);
p[x].mx[i]=max(p[x].mx[i],p[r].mx[i]);
}
}
}
int get_min(node x){
int sum=0;
for(int i=0;i<=1;i++){
sum+=max(x.mn[i]-t.d[i],0);
sum+=max(t.d[i]-x.mx[i],0);
}
return sum;
}
int get_max(node x){
int sum=0;
for(int i=0;i<=1;i++){
sum+=max(abs(x.mn[i]-t.d[i]),abs(x.mx[i]-t.d[i]));
}
return sum;
}
public:
void build(int &rt,int l,int r,int d){
int mid=l+r>>1;
now=d;
nth_element(tr+l,tr+mid,tr+r+1);
p[mid]=tr[mid];
for(int i=0;i<=1;i++)
p[mid].mx[i]=p[mid].mn[i]=p[mid].d[i];
if(l<mid) build(p[mid].l,l,mid-1,d^1);
if(r>mid) build(p[mid].r,mid+1,r,d^1);
update(mid);
rt=mid;
}
void query_min(int k){
int dist=dis(p[k],t);
if(dist) ans=min(ans,dist);
int sum_l=inf,sum_r=inf;
if(p[k].l) sum_l=get_min(p[p[k].l]);
if(p[k].r) sum_r=get_min(p[p[k].r]);
if(sum_l>sum_r){
if(sum_r<ans) query_min(p[k].r);
if(sum_l<ans) query_min(p[k].l);
}else{
if(sum_l<ans) query_min(p[k].l);
if(sum_r<ans) query_min(p[k].r);
}
}
void query_max(int k){
ans=max(ans,dis(p[k],t));
int sum_l=-inf,sum_r=-inf;
if(p[k].l) sum_l=get_max(p[p[k].l]);
if(p[k].r) sum_r=get_max(p[p[k].r]);
if(sum_l>sum_r){
if(sum_l>ans) query_max(p[k].l);
if(sum_r>ans) query_max(p[k].r);
}else{
if(sum_r>ans) query_max(p[k].r);
if(sum_l>ans) query_max(p[k].l);
}
}
}KD_Tree;
int main(){
n=read();
for(int i=1;i<=n;i++){
tr[i].d[0]=read();
tr[i].d[1]=read();
}
KD_Tree.build(root,1,n,0);
for(int i=1;i<=n;i++){
KD_Tree.t=tr[i];
KD_Tree.ans=inf;
KD_Tree.query_min(root);
int minn=KD_Tree.ans;
KD_Tree.ans=-inf;
KD_Tree.query_max(root);
int maxx=KD_Tree.ans;
ans=min(ans,maxx-minn);
}
printf("%d\n",ans);
return 0;
}

例题:JZPFAR:

题目大意:给定平面上n个点坐标以及m次询问,每一次输出欧式距离距目标点第k大的点的标号

跟上一个差不多,估价函数变了,

第k大的话,维护一个有k个元素的堆,每次有更优的就pop队顶,最终top就是答案

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define MAXN 500005
#define inf 0x7fffffff
#define int long long
using namespace std;
int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a<b?a:b;}
int abs(int a){return a<0?-a:a;}
int power(int a){return a*a;}
int n,m,now,root,k;
struct data{
int dis,id;
friend bool operator < (data a,data b){
return a.dis==b.dis?a.id<a.id:a.dis>b.dis;
}
};
priority_queue< data > q;
struct node{
int d[2],l,r,mx[2],mn[2],id;
friend bool operator < (node a,node b){
return a.d[now]<b.d[now];
}
}ask,tr[MAXN],p[MAXN];
struct KD_TREE{
private:
void update(int x){
int l=tr[x].l,r=tr[x].r;
for(int i=0;i<=1;i++){
tr[x].mn[i]=tr[x].mx[i]=tr[x].d[i];
if(l!=0){
tr[x].mn[i]=min(tr[x].mn[i],tr[l].mn[i]);
tr[x].mx[i]=max(tr[x].mx[i],tr[l].mx[i]);
}
if(r!=0){
tr[x].mn[i]=min(tr[x].mn[i],tr[r].mn[i]);
tr[x].mx[i]=max(tr[x].mx[i],tr[r].mx[i]);
}
}
}
int dis(node a,node b){
int res=0;
for(int i=0;i<=1;i++){
res+=power(a.d[i]-b.d[i]);
}
return res;
}
int calc(int x){
if(x==0) return -2;
int res=0;
for(int i=0;i<=1;i++){
res+=max(power(tr[x].mx[i]-ask.d[i]),power(tr[x].mn[i]-ask.d[i]));
}
return res;
}
public:
void build(int &rt,int l,int r,int d){
int mid=(l+r)>>1;
now=d;
nth_element(p+l,p+mid,p+r+1);
tr[mid]=p[mid];
if(l<mid) build(tr[mid].l,l,mid-1,d^1);
if(r>mid) build(tr[mid].r,mid+1,r,d^1);
update(mid);
rt=mid;
}
void query(int x){
//cout<<x<<endl;
if(!x) return ;
int sum_l=calc(tr[x].l),sum_r=calc(tr[x].r),dist=dis(tr[x],ask);
//cout<<sum_l<<' '<<sum_r<<' '<<dist<<endl;
if(dist>q.top().dis||(dist==q.top().dis&&tr[x].id<q.top().id)){
q.pop();
//cout<<dist<<' '<<tr[x].id<<endl;
q.push((data){dist,tr[x].id});
}
if(sum_l>sum_r){
if(sum_l>=q.top().dis) query(tr[x].l);
if(sum_r>=q.top().dis) query(tr[x].r);
}else{
if(sum_r>=q.top().dis) query(tr[x].r);
if(sum_l>=q.top().dis) query(tr[x].l);
}
}
}KD_Tree;
signed main(){
n=read();
for(int i=1;i<=n;i++){
p[i].d[0]=read();
p[i].d[1]=read();
p[i].id=i;
}
KD_Tree.build(root,1,n,0);
//cout<<root<<endl;
m=read();
for(int i=1;i<=m;i++){
ask.d[0]=read();
ask.d[1]=read();
k=read();
while(!q.empty()) q.pop();
for(int i=1;i<=k;i++)
q.push((data){-1,0});
// q.push(make_pair(-1,0));
KD_Tree.query(root);
//cout<<q.top().first<<endl;
printf("%lld\n",q.top().id);
}
return 0;
}

进阶:K远点对

题目大意:已知平面内 N 个点的坐标,求欧氏距离下的第 K 远点对。

和上一个一样,只不过这一次我们要对每个点query一遍,

其中维护一个2k的堆(因为会重复算),最终堆顶就是答案

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define MAXN 100005
#define inf 0x7fffffff
#define int long long
using namespace std;
int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a<b?a:b;}
int abs(int a){return a<0?-a:a;}
int power(int a){return a*a;}
int n,m,now,root,k;
struct data{
int dis;
friend bool operator < (data a,data b){
return a.dis<b.dis;
}
};
priority_queue<int> q;
struct node{
int d[2],l,r,mx[2],mn[2],id;
friend bool operator < (node a,node b){
return a.d[now]<b.d[now];
}
}ask,tr[MAXN];
struct KD_TREE{
void update(int x){
int l=tr[x].l,r=tr[x].r;
for(int i=0;i<=1;i++){
tr[x].mn[i]=tr[x].mx[i]=tr[x].d[i];
if(l!=0){
tr[x].mn[i]=min(tr[x].mn[i],tr[l].mn[i]);
tr[x].mx[i]=max(tr[x].mx[i],tr[l].mx[i]);
}
if(r!=0){
tr[x].mn[i]=min(tr[x].mn[i],tr[r].mn[i]);
tr[x].mx[i]=max(tr[x].mx[i],tr[r].mx[i]);
}
}
}
int dis(node a,node b){
int res=0;
for(int i=0;i<=1;i++){
res+=power(a.d[i]-b.d[i]);
}
return res;
}
int get_dis(node a){
int res=0;
for(int i=0;i<=1;i++){
res+=max(power(a.mx[i]-ask.d[i]),power(a.mn[i]-ask.d[i]));
}
return res;
}
void build(int &rt,int l,int r,int d){
int mid=(l+r)>>1;
now=d;
nth_element(tr+l,tr+mid,tr+r+1);
if(l<mid) build(tr[mid].l,l,mid-1,d^1);
if(r>mid) build(tr[mid].r,mid+1,r,d^1);
update(mid);
rt=mid;
}
void query(int x){
if(!x) return ;
int sum_l=inf,sum_r=inf,dist=dis(tr[x],ask);
if(tr[x].l) sum_l=get_dis(tr[tr[x].l]);
if(tr[x].r) sum_r=get_dis(tr[tr[x].r]);
if(dist>-q.top()){
q.pop();
q.push(-dist);
}
if(sum_l>sum_r){
if(sum_l>=-q.top()) query(tr[x].l);
if(sum_r>=-q.top()) query(tr[x].r);
}else{
if(sum_r>=-q.top()) query(tr[x].r);
if(sum_l>=-q.top()) query(tr[x].l);
}
}
}KD_Tree;
signed main(){
n=read(),k=read();
for(int i=1;i<=n;i++){
tr[i].d[0]=read();
tr[i].d[1]=read();
tr[i].id=i;
}
KD_Tree.build(root,1,n,0);
for(int i=1;i<=2*k;i++) q.push(inf);
for(int i=1;i<=n;i++){
ask=tr[i];
KD_Tree.query(root);
}
printf("%lld\n",-q.top());
return 0;
}

模板:KD-Tree的更多相关文章

  1. [模板] K-D Tree

    K-D Tree K-D Tree可以看作二叉搜索树的高维推广, 它的第 \(k\) 层以所有点的第 \(k\) 维作为关键字对点做出划分. 为了保证划分均匀, 可以以第 \(k\) 维排名在中间的节 ...

  2. k-d tree模板练习

    1. [BZOJ]1941: [Sdoi2010]Hide and Seek 题目大意:给出n个二维平面上的点,一个点的权值是它到其他点的最长距离减最短距离,距离为曼哈顿距离,求最小权值.(n< ...

  3. AOJ DSL_2_C Range Search (kD Tree)

    Range Search (kD Tree) The range search problem consists of a set of attributed records S to determi ...

  4. 【BZOJ-2648&2716】SJY摆棋子&天使玩偶 KD Tree

    2648: SJY摆棋子 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 2459  Solved: 834[Submit][Status][Discu ...

  5. BZOJ2648/2716:SJY摆棋子/[Violet]天使玩偶(K-D Tree)

    Description 这天,SJY显得无聊.在家自己玩.在一个棋盘上,有N个黑色棋子.他每次要么放到棋盘上一个黑色棋子,要么放上一个白色棋子,如果是白色棋子,他会找出距离这个白色棋子最近的黑色棋子. ...

  6. k-d tree 学习笔记

    以下是一些奇怪的链接有兴趣的可以看看: https://blog.sengxian.com/algorithms/k-dimensional-tree http://zgjkt.blog.uoj.ac ...

  7. K-D Tree

    这篇随笔是对Wikipedia上k-d tree词条的摘录, 我认为解释得相当生动详细, 是一篇不可多得的好文. Overview A \(k\)-d tree (short for \(k\)-di ...

  8. K-D Tree题目泛做(CXJ第二轮)

    题目1: BZOJ 2716 题目大意:给出N个二维平面上的点,M个操作,分为插入一个新点和询问到一个点最近点的Manhatan距离是多少. 算法讨论: K-D Tree 裸题,有插入操作. #inc ...

  9. k-d Tree in TripAdvisor

    Today, TripAdvisor held a tech talk in Columbia University. The topic is about k-d Tree implemented ...

  10. k-d tree算法

    k-d树(k-dimensional树的简称),是一种分割k维数据空间的数据结构.主要应用于多维空间关键数据的搜索(如:范围搜索和最近邻搜索). 应用背景 SIFT算法中做特征点匹配的时候就会利用到k ...

随机推荐

  1. 解析JQuery Ajax

    jQuery是一个挺好的轻量级的JS框架,能帮助我们快速的开发JS应用,并在一定程度上改变了我们写JavaScript代码的习惯. 先来看一些简单的方法,这些方法都是对jQuery.ajax()进行封 ...

  2. 机器学习 101 Mahout 简介 建立一个推荐引擎 使用 Mahout 实现集群 使用 Mahout 实现内容分类 结束语 下载资源

      机器学习 101 Mahout 简介 建立一个推荐引擎 使用 Mahout 实现集群 使用 Mahout 实现内容分类 结束语 下载资源 相关主题   在信息时代,公司和个人的成功越来越依赖于迅速 ...

  3. SpringBoot:目录

    ylbtech-SpringBoot:目录 1.返回顶部   2.返回顶部   3.返回顶部   4.返回顶部   5.返回顶部     6.返回顶部   作者:ylbtech出处:http://yl ...

  4. django2 rest_framework + vue.js + mysql5.6 实现增删改查

    1.安装pymysql,mysqlclient,创建项目django-admin startproject django3 2.在Mysql中创建一个数据库叫django3db,打开项目,修改一下数据 ...

  5. (36)C# COM交互

    调用DLL [DllImport("standerMFC.dll")] public static extern int PReadUID(ref HHFC_SET stru); ...

  6. day22_4-pickle模块

    # 参考资料:# python模块(转自Yuan先生) - 狂奔__蜗牛 - 博客园# https://www.cnblogs.com/guojintao/articles/9070485.html ...

  7. SQL SERVER 备份数据库时候错误处理

    当备份数据库时候出现如下错误时候 只需要删除备份目标就行了

  8. gradle 排除冲突依赖包

    1 , 如何查看jar包依赖源 2 结果显示: 3 排除

  9. <数据分析>初级入门

    1.何为数据分析? 数据分析是指用适当的统计方法对收集来的大量数据进行分析,将它们加以汇总和理解消化,以求最大化地开发数据的功能,发挥数据的作用. 直接的理解:提炼杂乱无章的数据背后的信息,总结出研究 ...

  10. java关键字之instanceof

    首先来看段测试代码 public class TestInstanceof{ public static void main(String[] args){ int a = 1; if(a insta ...