链接
KD-Tree 基础练习题 - 题单 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
【KD Tree_CCPC/ICPC区域赛知识点练习_牛客竞赛OJ】 (nowcoder.com)
1.寻找近邻点
In case of failure(离线 + 最近邻点)
题意:给出n个点(x,y),求n个点的最近邻点
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 10, K = 2;//点数,维度
struct Point{int dim[K];};
Point q[N], t[N];//q记录输入n个点的坐标,t存储二叉树
int now;//当前的维度
bool cmp(Point a, Point b){return a.dim[now] < b.dim[now];};//第now维比较
int square(int x){return x * x;}
int dis(Point a, Point b)
{
int ans = 0;
for(int i = 0; i < K; i ++) ans += square(a.dim[i] - b.dim[i]);
return ans;
}
void build(int L, int R, int dep)
{
if(L >= R) return;
int d = dep % K;//轮转法,dep为当前层的深度,d为当前层的维度
int mid = L + R >> 1;
now = d;
nth_element(t + L, t + mid, t + R, cmp);
build(L, mid, dep + 1);
build(mid + 1, R, dep + 1);
}
int ans;
void query(int L, int R, int dep, Point p)
{
if(L >= R) return;
int mid = L + R >> 1;
int d = dep % K;//轮转法
int mindis = dis(t[mid], p);//这棵子树的根到p的最小距离
if(ans == 0) ans = mindis;//赋初值
if(mindis != 0 && ans > mindis) ans = mindis;//需要特判t[mid]和p重合的情况
if(p.dim[d] > t[mid].dim[d])//在这个维度,p大于子树的根,接下来查右子树
{
query(mid + 1, R, dep + 1, p);
if(ans > square(t[mid].dim[d] - p.dim[d])) query(L, mid, dep + 1, p);//如果以ans为半径的圆与左子树相交,那么左子树也要查
}
else
{
query(L, mid, dep + 1, p);//在这个维度,p小于子树的根,接下来查左子树
if(ans > square(t[mid].dim[d] - p.dim[d])) query(mid + 1, R, dep + 1, p);//右子树也要查
}
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
//cout.tie(0);
int T; cin >> T;
while(T --)
{
int n; cin >> n;
for(int i = 0; i < n; i ++) cin >> q[i].dim[0] >> q[i].dim[1], t[i] = q[i];
build(0, n, 0);//建树
for(int i = 0; i < n; i ++)
{
ans = 0;
query(0, n, 0, q[i]);
cout << ans << '\n';
}
}
}
[Violet] 天使玩偶/SJY摆棋子 - 洛谷(在线替罪羊树拍平 + 最近邻点)
拍平还没学会,之后会更上。
2.区间查询
简单题 - 洛谷(在线替罪羊树拍平 + 区间查询)
3.偏序
[CH弱省胡策R2] TATT - 洛谷(四维偏序)
4.杂
K远点对
题意:已知平面内 N 个点的坐标,求欧氏距离下的第 K 远点对。
#include<bits/stdc++.h>
#define int long long
#define square(x) ((x)*(x))
using namespace std;
const int N = 4e5 + 10, inf = 1e18;
struct point
{
int dim[2];
}w[N], p[N];
int n, k, cnt, d, mx[N][2], mi[N][2], ls[N], rs[N];
priority_queue<int> q;
bool cmp(point x, point y){return x.dim[d] < y.dim[d];}
int gdis(point x, point y){return square(x.dim[0]-y.dim[0]) + square(x.dim[1]-y.dim[1]);}
int limd(point x, int y){return max(square(x.dim[0]-mx[y][0]), square(x.dim[0]-mi[y][0])) + max(square(x.dim[1]-mx[y][1]), square(x.dim[1]-mi[y][1]));}
void PushUp(int x)
{
int l = ls[x], r = rs[x];//左右孩子
mx[x][0] = mi[x][0] = w[x].dim[0];
mx[x][1] = mi[x][1] = w[x].dim[1];
if(l)
{
for(int i = 0; i < 2; i ++) mx[x][i] = max(mx[x][i], mx[l][i]), mi[x][i] = min(mi[x][i], mi[l][i]);
}
if(r)
{
for(int i = 0; i < 2; i ++) mx[x][i] = max(mx[x][i], mx[r][i]), mi[x][i] = min(mi[x][i], mi[r][i]);
}
}
void Build(int &x, int l, int r, int z)
{
if(l > r) return;
x = ++cnt;//给点编号
int mid = (l + r) >> 1; d = z;
nth_element(p + l, p + mid, p + r + 1, cmp);
w[x] = p[mid];//建树
Build(ls[x], l, mid - 1, z ^ 1);
Build(rs[x], mid + 1, r, z ^ 1);
PushUp(x);
}
void Query(int x, point v)//求每个点与它距离最远的点
{
if(!x) return;
int dl = -inf, dr = -inf;
if(ls[x]) dl = limd(v, ls[x]);
if(rs[x]) dr = limd(v, rs[x]);
int distance = gdis(w[x], v);
if(distance > -q.top()) q.pop(), q.push(-distance);
if(dl > -q.top()) Query(ls[x], v);//左孩子的距离大于当前最大距离,左搜
if(dr > -q.top()) Query(rs[x], v);//同理,右搜
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin >> n >> k;
for(int i = 1; i <= n; i ++) cin >> p[i].dim[0] >> p[i].dim[1];
int rt; Build(rt, 1, n, 0);
for(int i = 1; i <= 2 * k; i ++) q.push(0);
for(int i = 1; i <= n; i ++) Query(1, p[i]);
cout << -q.top() << '\n';
}