【高级数据结构】kd-tree题单

链接

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';
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值