[基本操作] kd 树
概念就不说了吧,网上教程满天飞
学了半天才知道,kd 树实质上只干了两件事情:
1.快速定位一个点 / 矩形
2.有理有据地优化暴力
第一点大概是可以来做二维平面上给点/矩形打标记的问题
第二点大概是平面最远点对?
bzoj1941 Hide and Seek
求每个点除自己以外的最近点和最远点
sol:
kd 树优化暴力,对于暴力,考虑这样一个剪枝:如果一个点在某一维上隔的太远,就不搜比它远的了
把这个东西放到 kd 树上就可以了
#include<bits/stdc++.h>
#define LL long long
using namespace std;
inline int read()
{
int x = ,f = ;char ch = getchar();
for(;!isdigit(ch);ch = getchar())if(ch == '-') f = -f;
for(;isdigit(ch);ch = getchar())x = * x + ch - '';
return x * f;
}
const int maxn = ,inf = 1e9;
#define L ps[x].lc
#define R ps[x].rc
int n,dem,root;
struct Node
{
int x[],lc,rc,mx[],mn[];
bool operator < (const Node &b)const{return x[dem] < b.x[dem];}
friend int dis(Node a,Node b){return abs(a.x[] - b.x[]) + abs(a.x[] - b.x[]);}
}ps[maxn];
int qx[maxn],qy[maxn];
void maintain(int x)
{
for(int i=;i<;i++)
{
ps[x].mn[i] = ps[x].mx[i] = ps[x].x[i];
if(L)ps[x].mx[i] = max(ps[x].mx[i],ps[L].mx[i]);
if(L)ps[x].mn[i] = min(ps[x].mn[i],ps[L].mn[i]);
if(R)ps[x].mx[i] = max(ps[x].mx[i],ps[R].mx[i]);
if(R)ps[x].mn[i] = min(ps[x].mn[i],ps[R].mn[i]);
}
}
void build(int &x,int l,int r,int cur)
{
if(l > r)return;
dem = cur;
int mid = (l + r) >> ;x = mid;
nth_element(ps + l,ps + mid,ps + r + );
build(L,l,mid - ,cur ^ );build(R,mid + ,r,cur ^ );
maintain(x);
}
int calmin(Node p,Node cur)
{
int ans = ;
for(int i=;i<;i++)
{
ans += max(p.x[i] - cur.mx[i],);
ans += max(-(p.x[i] - cur.mn[i]),);
}
return ans;
}
int calmax(Node p,Node cur)
{
int ans = ;
for(int i=;i<;i++)
ans += max(abs(p.x[i] - cur.mx[i]),abs(p.x[i] - cur.mn[i]));
return ans;
}
int ans;
void querymx(Node cur,int x)
{
ans = max(ans,dis(cur,ps[x]));
int dl = -inf,dr = -inf;
if(L)dl = calmax(cur,ps[L]);if(R)dr = calmax(cur,ps[R]);
if(dl > dr)
{
if(dl > ans)querymx(cur,L);
if(dr > ans)querymx(cur,R);
}
else
{
if(dr > ans)querymx(cur,R);
if(dl > ans)querymx(cur,L);
}
}
void querymn(Node cur,int x)
{
int tmp = dis(cur,ps[x]);
if(tmp)ans = min(ans,tmp);
int dl = inf,dr = inf;
if(L)dl = calmin(cur,ps[L]);if(R)dr = calmin(cur,ps[R]);
if(dl < dr)
{
if(dl < ans)querymn(cur,L);
if(dr < ans)querymn(cur,R);
}
else
{
if(dr < ans)querymn(cur,R);
if(dl < ans)querymn(cur,L);
}
}
int query(int x,int y,int type)
{
Node cur;
cur.x[] = x;cur.x[] = y;
if(type == )ans = inf,querymn(cur,root);
else ans = -inf,querymx(cur,root);
return ans;
}
int main()
{
n = read();
for(int i=;i<=n;i++)
{
qx[i] = ps[i].x[] = read();
qy[i] = ps[i].x[] = read();
}build(root,,n,);
int ret = ;
for(int i=;i<=n;i++)
{
int mn = query(qx[i],qy[i],),mx = query(qx[i],qy[i],);
ret = min(ret,mx - mn);
}cout<<ret<<endl;
}
bzoj4520 K 远点对
给 n 个点的坐标,求第 k 远的点对距离
sol:
暴力的话,用一个长度只有 k 的优先队列维护前 k 远距离
kd 树的话就是用这个做估价函数,如果当前搜到的点进不了队,就不搜了
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,k;
namespace KD_Tree
{
int dim;
priority_queue<ll,vector<ll>,greater<ll> >q;
void mdf(ll x){q.push(x);q.pop();}
struct Point
{
ll P[];
int ls,rs;
ll mn[],mx[];
}c[];
bool cmp(Point a,Point b){return a.P[dim]<b.P[dim];}
ll sqr(ll x) {return x*x;}
ll calc(Point x,Point y){return sqr(x.P[]-y.P[])+sqr(x.P[]-y.P[]);}
void update(int x)
{
c[x].mn[]=c[x].mx[]=c[x].P[];
c[x].mn[]=c[x].mx[]=c[x].P[];
for(int i=;i<;i++)
{
if(c[x].ls)
{
c[x].mn[i]=min(c[x].mn[i],c[c[x].ls].mn[i]);
c[x].mx[i]=max(c[x].mx[i],c[c[x].ls].mx[i]);
}
if(c[x].rs)
{
c[x].mn[i]=min(c[x].mn[i],c[c[x].rs].mn[i]);
c[x].mx[i]=max(c[x].mx[i],c[c[x].rs].mx[i]);
}
}
}
int build(int l,int r,int w)
{
if(l>r) return ;
if(l==r) {update(l);return l;}
dim=w;int mid=(l+r)>>;
nth_element(c+l,c+mid,c+r+,cmp);
c[mid].ls=build(l,mid-,w^);c[mid].rs=build(mid+,r,w^);
update(mid);
return mid;
}
ll cheque(int x,Point p)
{
ll tx=max(sqr(c[x].mn[]-p.P[]),sqr(c[x].mx[]-p.P[]));
ll ty=max(sqr(c[x].mn[]-p.P[]),sqr(c[x].mx[]-p.P[]));
return tx+ty;
}
void qury(int x,Point p)
{
mdf(calc(c[x],p));
ll dl=,dr=;
if(c[x].ls) dl=cheque(c[x].ls,p);
if(c[x].rs) dr=cheque(c[x].rs,p);
if(dl>dr)
{
if(dl>q.top()) qury(c[x].ls,p);
if(dr>q.top()) qury(c[x].rs,p);
}
else
{
if(dr>q.top()) qury(c[x].rs,p);
if(dl>q.top()) qury(c[x].ls,p);
}
}
}
using namespace KD_Tree;
int main()
{
scanf("%d%d",&n,&k);
for(int i=;i<=*k;i++) q.push();
for(int i=;i<=n;i++)
{
scanf("%lld%lld",&c[i].P[],&c[i].P[]);
}
int rt=build(,n,);
for(int i=;i<=n;i++)
{
qury(rt,c[i]);
}
printf("%lld\n",q.top());
}
bzoj2683 & bzoj4066 简单题
矩形加,矩形求和
sol:
这道题要支持在 kd 树里插入一个点,类似二叉搜索树,走到儿子然后把新点加进去就可以了
注意定期重构
重构的时候注意走到非法节点就把它赋成 0
#include<bits/stdc++.h>
#define LL long long
using namespace std;
inline int read()
{
int x = ,f = ;char ch = getchar();
for(;!isdigit(ch);ch = getchar())if(ch == '-') f = -f;
for(;isdigit(ch);ch = getchar())x = * x + ch - '';
return x * f;
}
const int maxn = ,inf = 1e9;
#define L (ps[x].lc)
#define R (ps[x].rc)
int n,dem,root,ToT;
struct Node
{
int x[],lc,rc,mx[],mn[],val,sum;
bool operator < (const Node &b)const{return x[dem] < b.x[dem];}
bool operator == (const Node& t) const { return x[] == t.x[] && x[] == t.x[]; }
//friend int dis(Node a,Node b){return abs(a.x[0] - b.x[0]) + abs(a.x[1] - b.x[1]);}
}ps[maxn];
void maintain(int x)
{
for(int i=;i<;i++)
{
ps[x].mn[i] = ps[x].mx[i] = ps[x].x[i];
//cout<<L<<" "<<R<<endl;
if(L)ps[x].mx[i] = max(ps[x].mx[i],ps[L].mx[i]);
if(L)ps[x].mn[i] = min(ps[x].mn[i],ps[L].mn[i]);
if(R)ps[x].mx[i] = max(ps[x].mx[i],ps[R].mx[i]);
if(R)ps[x].mn[i] = min(ps[x].mn[i],ps[R].mn[i]);
}
ps[x].sum = ps[x].val;
if(L)ps[x].sum += ps[L].sum;
if(R)ps[x].sum += ps[R].sum;
}
void build(int &x,int l,int r,int cur)
{
if(l > r){x = ;return;}
dem = cur;
int mid = (l + r) >> ;x = mid;
nth_element(ps + l,ps + mid,ps + r + );
build(L,l,mid - ,cur ^ );build(R,mid + ,r,cur ^ );
maintain(x);
}
void insert(int &x,Node cur,int cur_d)
{
if(!x)
{
x = ++ToT;
ps[x] = cur;
maintain(x);
return;
}
if(ps[x] == cur)
{
ps[x].val += cur.val;
ps[x].sum += cur.val;
maintain(x);
return;
}
if(cur.x[cur_d] < ps[x].x[cur_d])insert(L,cur,cur_d ^ );
else insert(R,cur,cur_d ^ );
maintain(x);
}
inline int all_in(Node cur_1,Node cur_2,int x){return (cur_1.x[] <= ps[x].mn[]) && (ps[x].mx[] <= cur_2.x[]) && (cur_1.x[] <= ps[x].mn[]) && (ps[x].mx[] <= cur_2.x[]);}
inline int has_node(Node cur_1,Node cur_2,int x){return !(ps[x].mx[] < cur_1.x[] || ps[x].mn[] > cur_2.x[] || ps[x].mx[] < cur_1.x[] || ps[x].mn[] > cur_2.x[]);}
inline int query(Node cur_1,Node cur_2,int x)
{
if(!x)return ;
int ans = ;
if(all_in(cur_1,cur_2,L))ans += ps[L].sum;
else if(has_node(cur_1,cur_2,L)) ans += query(cur_1,cur_2,L);
if(all_in(cur_1,cur_2,R))ans += ps[R].sum;
else if(has_node(cur_1,cur_2,R)) ans += query(cur_1,cur_2,R);
int nx = ps[x].x[],ny = ps[x].x[];
if(cur_1.x[] <= nx && nx <= cur_2.x[] && cur_1.x[] <= ny && ny <= cur_2.x[])ans += ps[x].val;
return ans;
}Node cur_1,cur_2;
int main()
{
//nodes[0].val = nodes[0].sum = 0;
n = read();int lastans = ;
n = read();
while(n != )
{
if(n == )
{
cur_1.x[] = read() ^ lastans;
cur_1.x[] = read() ^ lastans;
cur_1.val = read() ^ lastans;
insert(root,cur_1,);if(ToT % == )build(root,,ToT,);
}
if(n == )
{
cur_1.x[] = read() ^ lastans;cur_1.x[] = read() ^ lastans;
cur_2.x[] = read() ^ lastans;cur_2.x[] = read() ^ lastans;
printf("%d\n",lastans = query(cur_1,cur_2,root));
}
n = read();
}
}
bzoj4170 & 2989 数列
给一个二维平面,每次插入一个单点,查询与给定点曼哈顿距离不超过 $d$ 的点的数量
sol:
实质上是查询一个斜 45 度的正方形内有多少个点,这个东西直接做复杂度好像是错的,旋转一下坐标系即可
(然而你为啥不写 CDQ 呢
#include<bits/stdc++.h>
#define LL long long
using namespace std;
inline int read()
{
int x = ,f = ;char ch = getchar();
for(;!isdigit(ch);ch = getchar())if(ch == '-') f = -f;
for(;isdigit(ch);ch = getchar())x = * x + ch - '';
return x * f;
}
const int maxn = ;
int n,q,dem,ToT,root;
int a[maxn];
#define L tr[x].ls
#define R tr[x].rs
struct Node
{
int x[],ls,rs,mx[],mn[],val,sum;
bool operator < (const Node &b)const{return x[dem] < b.x[dem];}
bool operator == (const Node &b)const{return (x[] == b.x[]) && (x[] == b.x[]);}
}tr[maxn];
void maintain(int x)
{
for(int i=;i<;i++)
{
tr[x].mx[i] = tr[x].mn[i] = tr[x].x[i];
if(L)tr[x].mx[i] = max(tr[L].mx[i],tr[x].mx[i]);
if(L)tr[x].mn[i] = min(tr[L].mn[i],tr[x].mn[i]);
if(R)tr[x].mx[i] = max(tr[R].mx[i],tr[x].mx[i]);
if(R)tr[x].mn[i] = min(tr[R].mn[i],tr[x].mn[i]);
}tr[x].sum = tr[x].val;
if(L)tr[x].sum += tr[L].sum;
if(R)tr[x].sum += tr[R].sum;
}
void build(int &x,int l,int r,int cur_d)
{
if(l > r){x = ;return;}
dem = cur_d;
int mid = (l + r) >> ;
nth_element(tr + l,tr + mid,tr + r + );
x = mid;//tr[x].val = 1;
cur_d = cur_d ^ ;
build(L,l,mid - ,cur_d);build(R,mid + ,r,cur_d);
maintain(x);
}
void insert(int &x,Node cur,int cur_d)
{
if(!x)
{
x = ++ToT;
tr[x] = cur;
maintain(x);
return;
}
if(tr[x] == cur)
{
tr[x].val += cur.val;
tr[x].sum += cur.val;
maintain(x);
return;
}
if(cur.x[cur_d] < tr[x].x[cur_d])insert(L,cur,cur_d ^ );
else insert(R,cur,cur_d ^ );
maintain(x);
}
inline int all_in(Node cur_1,Node cur_2,int x){return (cur_1.x[] <= tr[x].mn[]) && (tr[x].mx[] <= cur_2.x[]) && (cur_1.x[] <= tr[x].mn[]) && (tr[x].mx[] <= cur_2.x[]);}
inline int has_node(Node cur_1,Node cur_2,int x){return !(tr[x].mx[] < cur_1.x[] || tr[x].mn[] > cur_2.x[] || tr[x].mx[] < cur_1.x[] || tr[x].mn[] > cur_2.x[]);}
inline int query(Node cur_1,Node cur_2,int x)
{
if(!x)return ;
int ans = ;
if(all_in(cur_1,cur_2,L))ans += tr[L].sum;
else if(has_node(cur_1,cur_2,L)) ans += query(cur_1,cur_2,L);
if(all_in(cur_1,cur_2,R))ans += tr[R].sum;
else if(has_node(cur_1,cur_2,R)) ans += query(cur_1,cur_2,R);
int nx = tr[x].x[],ny = tr[x].x[];
if(cur_1.x[] <= nx && nx <= cur_2.x[] && cur_1.x[] <= ny && ny <= cur_2.x[])ans += tr[x].val;
return ans;
}Node cur_1,cur_2;
int main()
{
n = read();q = read();
for(int i=;i<=n;i++)a[i] = read();
for(int i=;i<=n;i++)
{
cur_1.x[] = i - a[i],cur_1.x[] = i + a[i],cur_1.val = ;
insert(root,cur_1,);
}build(root,,n,);
char opt[];
while(q--)
{
scanf("%s",opt + );
if(opt[] == 'M')
{
int x = read(),y = read();
a[x] = y;cur_1.x[] = x - y,cur_1.x[] = x + y,cur_1.val = ;
insert(root,cur_1,);if(ToT % == )build(root,,ToT,);
}
else
{
int x = read(),y = read();
cur_1.x[] = x - a[x] - y;
cur_2.x[] = x + a[x] + y;
cur_2.x[] = x - a[x] + y;
cur_1.x[] = x + a[x] - y;
//if(cur_1.x[0] > cur_2.x[0])swap(cur_1.x[0],cur_2.x[0]);
//if(cur_1.x[1] > cur_2.x[1])swap(cur_1.x[1],cur_2.x[1]);
printf("%d\n",query(cur_1,cur_2,root));
}
}
}
[基本操作] kd 树的更多相关文章
- 利用KD树进行异常检测
软件安全课程的一次实验,整理之后发出来共享. 什么是KD树 要说KD树,我们得先说一下什么是KNN算法. KNN是k-NearestNeighbor的简称,原理很简单:当你有一堆已经标注好的数据时,你 ...
- 2016 ICPC青岛站---k题 Finding Hotels(K-D树)
题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=5992 Problem Description There are N hotels all over ...
- kd树和knn算法的c语言实现
基于kd树的knn的实现原理可以参考文末的链接,都是一些好文章. 这里参考了别人的代码.用c语言写的包括kd树的构建与查找k近邻的程序. code: #include<stdio.h> # ...
- PCL点云库:Kd树
Kd树按空间划分生成叶子节点,各个叶子节点里存放点数据,其可以按半径搜索或邻区搜索.PCL中的Kd tree的基础数据结构使用了FLANN以便可以快速的进行邻区搜索.FLANN is a librar ...
- KNN算法与Kd树
最近邻法和k-近邻法 下面图片中只有三种豆,有三个豆是未知的种类,如何判定他们的种类? 提供一种思路,即:未知的豆离哪种豆最近就认为未知豆和该豆是同一种类.由此,我们引出最近邻算法的定义:为了判定未知 ...
- k临近法的实现:kd树
# coding:utf-8 import numpy as np import matplotlib.pyplot as plt T = [[2, 3], [5, 4], [9, 6], [4, 7 ...
- 从K近邻算法谈到KD树、SIFT+BBF算法
转自 http://blog.csdn.net/v_july_v/article/details/8203674 ,感谢july的辛勤劳动 前言 前两日,在微博上说:“到今天为止,我至少亏欠了3篇文章 ...
- bzoj 3489: A simple rmq problem k-d树思想大暴力
3489: A simple rmq problem Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 551 Solved: 170[Submit][ ...
- k近邻法的C++实现:kd树
1.k近邻算法的思想 给定一个训练集,对于新的输入实例,在训练集中找到与该实例最近的k个实例,这k个实例中的多数属于某个类,就把该输入实例分为这个类. 因为要找到最近的k个实例,所以计算输入实例与训练 ...
随机推荐
- python常用模块——os模块
python编程时,经常和文件.目录打交道,这就离不开os模块,os模块包含普遍的操作系统功能,与具体的平台无关,列举一些常用的命令. 1.os.name:字符串指示你正在使用的平台.windows是 ...
- ajax 请求后台数据返回异常 及 提示404方法名不存在
1.正常使用 Ajax 调取后台数据时,提示方法名不存在,Ajax前端正常,方法类bean注入正常,方法注解正常.但参数解析时出现异常. @RequestMapping(value="/ge ...
- node.js应用生成windows service的plugin——winser
from:http://xiaomijsj.blog.163.com/blog/static/89685520135854036206/ 针对项目中windows server machine 不断重 ...
- PHP基本语法,类基本函数
C#中函数四要素返回类型,函数名,参数列表,函数体pulic void show()php函数定义1.最简单的定义function show(){echo "hello"}show ...
- ibatis工作原理
摘要: iBATIS 通过 SQL Map 将 Java 对象映射成 SQL 语句和将结果集再转化成 Java 对象,与其他 ORM 框架相比,既解决了 Java 对象与输入参数和结果集的映射,又能够 ...
- 每天一个linux命令(6/18):lsof命令
lsof(list open files)是一个列出当前系统打开文件的工具.在linux环境下,任何事物都以文件的形式存在,通过文件不仅仅可以访问常规数据,还可以访问网络连接和硬件.所以,lsof的功 ...
- 在shell,R,python中用变量和常量创建文件名
很多时候我们希望文件名的格式是:变量+常量的. 1.shell:变量"常量" [wangjq@mgmt multi_pcr]$ a="var" [wangjq@ ...
- debian内核代码执行流程(一)
本文根据debian开机信息来查看内核源代码. 系统使用<debian下配置dynamic printk以及重新编译内核>中内核源码来查看执行流程. 使用dmesg命令,得到下面的开机信息 ...
- js学习笔记1(变量、作用域、内存)
写在前面,舍弃叽叽歪歪,只做学习笔记,认真踏实. 学习书籍:javascript高级程序设计3版. 章节4.1 基本类型和引用类型 1.基本类型在内存中占据固定大小的空间,所以保存在栈内存中. 2.从 ...
- 自动生成Mapper代码
public class BeanMapperTest { @Test public void build() throws Exception { Class clazz = RiskAccess. ...