[基本操作] 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个实例,所以计算输入实例与训练 ...
随机推荐
- form 表单<input type="button" value="登录" onclick="loginSubmit ()"/> 点击提示 Uncaught TypeError: loginSubmit is not a function
在网上搜了一堆东东,仔细看了一下,再加上实验,发现原因出在<form>中. <form method="post"> <button type=&qu ...
- [笔记]Go语言在Linux环境下输出彩色字符
Go语言要打印彩色字符与Linux终端输出彩色字符类似,以黑色背景高亮绿色字体为例: fmt.Printf("\n %c[1;40;32m%s%c[0m\n\n", 0x1B, & ...
- LeetCode_Easy_471:Number Complement
LeetCode_Easy_471:Number Complement 题目描述 Given a positive integer, output its complement number. The ...
- 使用idea2016导出web项目war包
第一步配置Web Application:Exploded(已经配置的可以跳到第二步): 打开project structure(默认的快捷键是Ctrl+Alt+Shift+S),依次选择Artifa ...
- $git学习总结系列(2)——远程仓库
本文主要介绍git本地仓库和GitHub远程仓库之间的交互和数据传输. 注:首先需要到github.com上注册一个账号. 1. 添加本地SSH Key到GitHub 要向GitHub远程仓库推送代码 ...
- .ssh中的文件的分别意义
当我们在用户的主目录使用如下命令: cd (进入个人主目录,默认为/home/hadoop) ssh-keygen -t rsa -P '' (注:最后是二个单引号) 表示在用户的主目录创建ssh登陆 ...
- nginx负载均衡详情
负载均衡是我们大流量网站要做的一个东西,下面我来给大家介绍在Nginx服务器上进行负载均衡配置方法,希望对有需要的同学有所帮助哦. 负载均衡 先来简单了解一下什么是负载均衡,单从字面上的意思来理解就可 ...
- 【HackerRank】Closest Numbers
Sorting is often useful as the first step in many different tasks. The most common task is to make f ...
- Squid 访问控制配置
Squid 访问控制配置 主配置文件内加入限制参数 vim /etc/squid/squid.conf # 访问控制 acl http proto HTTP # 限制访问 good_domain添加两 ...
- MySQL实验1: 新建一个名为 library 的数据库,包含 book、reader 两张表,根据自己的理解安排表的内容并插入数据。
数据表(table)简称表,它是数据库最重要的组成部分之一.数据库只是一个框架,表才是实质内容. 实验: 新建一个名为 library的数据库,包含 book.reader两张表,根据自己的理解安排表 ...