KD-Tree学习笔记

KD-Tree学习笔记


KD-Tree简介

KD-Tree的思想其实很简单,就是将点对依次按照横坐标、纵坐标、横坐标、纵坐标……分治,维护一些信息用于启发式搜索。貌似求最近点对的复杂度没什么保证,但确实非常快…

一般而言几种情况:

  1. 最近曼哈顿距离,维护 xmin,xmax,ymin,ymax ,则 (x,y) 的启发函数是 max(xminx,0)+max(xxmax,0)+max(yminy,0)+max(yymax,0)

  2. 最远曼哈顿距离,维护 xmin,xmax,ymin,ymax ,则 (x,y) 启发函数是 max(|xminx|,|xmaxx|)+max(|yminy|,|ymaxy|)

  3. 最近欧几里得距离/最远欧几里得距离,都加个平方即可。

bzoj1941: [Sdoi2010]Hide and Seek

曼哈顿距离kdtree模板题…

#include 
using namespace std;

const int MAXN = 500005;

struct point {
    int x, y;
    inline void update_max(const point &a)
    { x = max(x, a.x), y = max(y, a.y); }
    inline void update_min(const point &a)
    { x = min(x, a.x), y = min(y, a.y); }
} pts[MAXN];

inline bool cmpx(const point &a, const point &b)
{ return a.x < b.x; }
inline bool cmpy(const point &a, const point &b)
{ return a.y < b.y; }

struct node {
    point pt, pt_min, pt_max;
    int lc, rc;
    int split_mod;
    inline void update();
    inline int h_max(const point &a);
    inline int h_min(const point &a);
    inline int trans(const point &a);
} kdtree[MAXN];

int top = 0, root;
inline void node::update()
{
    if (lc) pt_min.update_min(kdtree[lc].pt_min), pt_max.update_max(kdtree[lc].pt_max);
    if (rc) pt_min.update_min(kdtree[rc].pt_min), pt_max.update_max(kdtree[rc].pt_max);
}

inline int node::h_max(const point &a)
{ return max(abs(a.x-pt_min.x), abs(a.x-pt_max.x))+max(abs(a.y-pt_min.y), abs(a.y-pt_max.y)); }

inline int node::h_min(const point &a)
{ return max(a.x-pt_max.x, 0)+max(pt_min.x-a.x, 0)+max(a.y-pt_max.y, 0)+max(pt_min.y-a.y, 0); }

inline int dis(int i, int j)
{ return abs(kdtree[i].pt.x-kdtree[j].pt.x)+abs(kdtree[i].pt.y-kdtree[j].pt.y); }

inline int node::trans(const point &a)
{
    if (split_mod == 0) return a.x <= pt.x;
    else return a.y <= pt.y;
}

void build(int &nd, int L, int R, int split_mod = 0)
{
    if (L > R) return;
    nd = ++top;
    int mid = (L+R)>>1;
    if (split_mod == 0) nth_element(pts+L, pts+mid, pts+R+1, cmpx);
    else nth_element(pts+L, pts+mid, pts+R+1, cmpy);
    kdtree[nd].pt = kdtree[nd].pt_min = kdtree[nd].pt_max = pts[mid];
    build(kdtree[nd].lc, L, mid-1, split_mod^1), build(kdtree[nd].rc, mid+1, R, split_mod^1);
    kdtree[nd].update();
}

int ans, n;

void query_min(int nd, const int pos)
{
    if (pos != nd) ans = min(ans, dis(nd, pos));
    int l = kdtree[nd].lc?kdtree[kdtree[nd].lc].h_min(kdtree[pos].pt):0, r = kdtree[nd].rc?kdtree[kdtree[nd].rc].h_min(kdtree[pos].pt):0;
    if (l < r) {
        if (kdtree[nd].lc && l < ans) query_min(kdtree[nd].lc, pos);
        if (kdtree[nd].rc && r < ans) query_min(kdtree[nd].rc, pos);
    } else {
        if (kdtree[nd].rc && r < ans) query_min(kdtree[nd].rc, pos);
        if (kdtree[nd].lc && l < ans) query_min(kdtree[nd].lc, pos);
    }
}

void query_max(int nd, const int pos)
{
    if (pos != nd) ans = max(ans, dis(nd, pos));
    int l = kdtree[nd].lc?kdtree[kdtree[nd].lc].h_max(kdtree[pos].pt):0, r = kdtree[nd].rc?kdtree[kdtree[nd].rc].h_max(kdtree[pos].pt):0;
    if (l > r) {
        if (kdtree[nd].lc && l > ans) query_max(kdtree[nd].lc, pos);
        if (kdtree[nd].rc && r > ans) query_max(kdtree[nd].rc, pos);
    } else {
        if (kdtree[nd].rc && r > ans) query_max(kdtree[nd].rc, pos);
        if (kdtree[nd].lc && l > ans) query_max(kdtree[nd].lc, pos);
    }
}

int qmax(int pos)
{
    ans = 0;
    query_max(root, pos);
    return ans;
}

int qmin(int pos)
{
    ans = 1e9;
    query_min(root, pos);
    return ans;
}

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) 
        scanf("%d%d", &pts[i].x, &pts[i].y);
    build(root, 1, n);
    int A = 1e9;
    for (int i = 1; i <= n; i++) {
        A = min(A, qmax(i)-qmin(i));
    }
    printf("%d\n", A);
    return 0;
}

loj#2043. 「CQOI2016」K 远点对

用一个堆优化静态kd-tree。如果当前距离比堆中最小的距离大,则更新;如果最大可能的距离比堆中距离小,则不再向下递归。

#include 
using namespace std;

const int MAXN = 100050;

struct point {
    long long x, y;
    inline void max_update(const point &a)
    { x = max(x, a.x), y = max(y, a.y); }
    inline void min_update(const point &a)
    { x = min(x, a.x), y = min(y, a.y); }
} pts[MAXN];
int n, root;
inline bool cmpx(const point &a, const point &b)
{ return a.x < b.x; }
inline bool cmpy(const point &a, const point &b)
{ return a.y < b.y; }

struct node {
    int lc, rc;
    point pt;
    point min_pt, max_pt;
    int split_mod;
    void update();
    long long h(const point &pt);
} kdtree[MAXN];
int top = 0;

void node::update()
{
    min_pt = max_pt = pt;
    if (lc) min_pt.min_update(kdtree[lc].min_pt), max_pt.max_update(kdtree[lc].max_pt);
    if (rc) min_pt.min_update(kdtree[rc].min_pt), max_pt.max_update(kdtree[rc].max_pt);
}

inline long long sqr_pow(long long nd)
{ return nd*nd; }

inline long long dis(int i, int j)
{ return sqr_pow(kdtree[i].pt.x-kdtree[j].pt.x)+sqr_pow(kdtree[i].pt.y-kdtree[j].pt.y); }

long long node::h(const point &pt)
{
    return max(sqr_pow(pt.x-min_pt.x), sqr_pow(pt.x-max_pt.x))+max(sqr_pow(pt.y-min_pt.y), sqr_pow(pt.y-max_pt.y));
}

void build(int &nd, int L, int R, int now_split = 0)
{
    if (L > R) return;
    nd = ++top;
    int mid = (L+R)>>1;
    nth_element(pts+L, pts+mid, pts+R+1, now_split==0?cmpx:cmpy);
    kdtree[nd].pt = kdtree[nd].min_pt = kdtree[nd].max_pt = pts[mid];
    if (L < R) {
        build(kdtree[nd].lc, L, mid-1, now_split^1), build(kdtree[nd].rc, mid+1, R, now_split^1);
        kdtree[nd].update();
    }
}

priority_queue<long long, vector<long long>, greater<long long> > q;

void query(int nd, int pos)
{
    long long d = dis(nd, pos);
    if (d > q.top()) q.pop(), q.push(d);
    long long L = kdtree[nd].lc?kdtree[kdtree[nd].lc].h(kdtree[pos].pt):0, R = kdtree[nd].rc?kdtree[kdtree[nd].rc].h(kdtree[pos].pt):0;
    if (L > R) {
        if (kdtree[nd].lc && L > q.top()) query(kdtree[nd].lc, pos);
        if (kdtree[nd].rc && R > q.top()) query(kdtree[nd].rc, pos); 
    } else {
        if (kdtree[nd].rc && R > q.top()) query(kdtree[nd].rc, pos);
        if (kdtree[nd].lc && L > q.top()) query(kdtree[nd].lc, pos);
    }
}

int k;
int main()
{
    scanf("%d%d", &n, &k);
    k *= 2;
    for (int i = 1; i <= n; i++)
        scanf("%lld%lld", &pts[i].x, &pts[i].y);
    build(root, 1, n);
    for (int i = 1; i <= k; i++) q.push(0);
    for (int i = 1; i <= n; i++)
        query(root, i);
    printf("%lld\n", q.top());
    return 0;
}

bzoj2648: SJY摆棋子

kdtree动态插入..

#include 
using namespace std;

const int MAXN = 1000005;

struct point {
    int x, y;
    inline void update_min(const point &a)
    { x = min(x, a.x), y = min(y, a.y); }
    inline void update_max(const point &a)
    { x = max(x, a.x), y = max(y, a.y); }
    friend bool operator == (const point &a, const point &b)
    { return a.x==b.x && a.y==b.y; }
    friend int operator * (const point &a, const point &b)
    { return abs(a.x-b.x)+abs(a.y-b.y); }
} pts[MAXN];
int pts_top = 0;

struct node {
    int lc, rc;
    point pt, min_pt, max_pt;
    int split_mod;
    inline void update();
    inline int h(const point &pt);
} kdtree[MAXN];
int top = 0, root = 0;

inline void node::update()
{
    if (lc) min_pt.update_min(kdtree[lc].min_pt), max_pt.update_max(kdtree[lc].max_pt);
    if (rc) min_pt.update_min(kdtree[rc].min_pt), max_pt.update_max(kdtree[rc].max_pt);
}

inline int node::h(const point &pt)
{ return max(min_pt.x-pt.x, 0)+max(pt.x-max_pt.x, 0)+max(min_pt.y-pt.y, 0)+max(pt.y-max_pt.y, 0); }

void push(int &nd, const point &pt, int split_mod = 0)
{
    if (!nd) nd = ++top, kdtree[nd] = (node){0, 0, pt, pt, pt, split_mod};
    else if (kdtree[nd].pt == pt) return;
    else {
        if (split_mod == 0) push(pt.x<=kdtree[nd].pt.x?kdtree[nd].lc:kdtree[nd].rc, pt, split_mod^1);
        else push(pt.y<=kdtree[nd].pt.y?kdtree[nd].lc:kdtree[nd].rc, pt, split_mod^1);
        kdtree[nd].update();
    }
}

int ans = 0;
void query(int nd, const point &pt)
{
    ans = min(ans, kdtree[nd].pt*pt);
    int L = kdtree[nd].lc?kdtree[kdtree[nd].lc].h(pt):0, R = kdtree[nd].rc?kdtree[kdtree[nd].rc].h(pt):0;
    if (L < R) {
        if (kdtree[nd].lc && L < ans) query(kdtree[nd].lc, pt);
        if (kdtree[nd].rc && R < ans) query(kdtree[nd].rc, pt);
    } else {
        if (kdtree[nd].rc && R < ans) query(kdtree[nd].rc, pt);
        if (kdtree[nd].lc && L < ans) query(kdtree[nd].lc, pt);
    }
}

inline int mindis(const point &pt)
{ return ans = INT_MAX, query(root, pt), ans; }

inline bool cmpx(const point &a, const point &b)
{ return a.x < b.x; }
inline bool cmpy(const point &a, const point &b)
{ return a.y < b.y; }

int get_split_mod(int L, int R)
{
    double avex = 0, avey = 0, sqx = 0, sqy = 0;
    for (register int i = L; i <= R; i++) 
        avex += pts[i].x, avey += pts[i].y;
    avex /= R-L+1, avey /= R-L+1;
    for (register int i = L; i <= R; i++)
        sqx += (pts[i].x-avex)*(pts[i].x-avex), sqy += (pts[i].y-avey)*(pts[i].y-avey);
    return sqx <= sqy;
}

void build(int &nd, int L, int R, int split_mod = 0)
{
    if (L > R) return;
    nd = ++top;
    int mid = (L+R)>>1;
    kdtree[nd].split_mod = split_mod;
    nth_element(pts+L, pts+mid, pts+R+1, kdtree[nd].split_mod?cmpy:cmpx);
    kdtree[nd].pt = kdtree[nd].min_pt = kdtree[nd].max_pt = pts[mid];
    kdtree[nd].lc = kdtree[nd].rc = 0;
    build(kdtree[nd].lc, L, mid-1, split_mod^1), build(kdtree[nd].rc, mid+1, R, split_mod^1);
    kdtree[nd].update();
}

inline void reconstruct()
{ top = 0, root = 0, build(root, 1, pts_top); }

int n, m, tp;
int x, y;

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) {
        scanf("%d%d", &x, &y);
        pts[++pts_top] = (point){x, y};
    }
    reconstruct();
    for (int i = 1; i <= m; i++) {
        scanf("%d%d%d", &tp, &x, &y);
        if (tp == 1) pts[++pts_top] = (point){x, y}, push(root, (point){x, y});
        else printf("%d\n", mindis((point){x, y}));
    }
    return 0;
}

你可能感兴趣的:(---数据结构---,奇奇怪怪的树)