K-D tree学习小记(贴模板的)

K-D tree是一棵平衡二叉树。

K_D即K-Dimention,k维的意思。

可能维护什么k维偏序。

建树:

建树的过程中每次会把一堆点按照某一维切半,中间的提出来作根,分成两个部分作为左右子树继续建树。

一个比较劣的维度选法是循环选取,雨露均沾。

也可以随机钦点。

正统的K-D tree是选方差最大的那一维。

复杂度: O ( k   n   l o g   n ) O(k~n~log~n) O(k n log n)

查询:

相当于把一个k维空间切切切,没有交集就退,完全包含就直接更新答案。

一次查询复杂度据说是 O ( n 1 − 1 k ) O(n^{1-{1 \over k}}) O(n1k1)

插入:

就是在tree上走到对应的位置,插进去就好了。

如果题目不是强制在线的,可以预先把所有的点建树,然后打tag来表示这个点有没有开启。

重构:

插入过多会导致平衡树不平衡,利用替罪羊树的思想重构就好了。

例题:
bzoj 3489: A simple rmq problem

Code:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
#define cmin(a, b) (a > b ? a = b : b)
#define cmax(a, b) (a < b ? a = b : b)
#define ls t[o].s[0]
#define rs t[o].s[1]
#define ll long long
using namespace std;
 
const int N = 1e5 + 5;
 
int n, m, x, y, la[N], c[N];
int D, rt;
 
struct no { int d[3];} a[N];
int cmp(no a, no b) {return a.d[D] < b.d[D];}
 
struct nod { int s[2], d[3], ma[3], mi[3], mx;
    void mer(nod a) {
        cmax(mx, a.mx);
        fo(i, 0, 2) cmax(ma[i], a.ma[i]), cmin(mi[i], a.mi[i]);
    }
} t[N];
 
ll s1[3], s2[3];
 
int get(int x, int y) {
    fo(i, 0, 2) s1[i] = s2[i] = 0;
    fo(i, x, y) fo(j, 0, 2) s1[j] += (ll) a[i].d[j] * a[i].d[j], s2[j] += a[i].d[j];
    fo(j, 0, 2) s1[j] = s1[j] * (y - x + 1) - s2[j] * s2[j];
    return s1[0] > s1[1] ? (s1[0] > s1[2] ? 0 : 2) : (s1[1] > s1[2] ? 1 : 2);
}
 
void bt(int &v, int x, int y) {
    int o = x + y >> 1; v = o; D = get(x, y);
    nth_element(a + x, a + o, a + y + 1, cmp);
    t[o].mx = c[a[o].d[0]];
    fo(j, 0, 2) t[o].ma[j] = t[o].mi[j] = a[o].d[j];
    if(x < o) bt(ls, x, o - 1), t[o].mer(t[ls]);
    if(o < y) bt(rs, o + 1, y), t[o].mer(t[rs]);
}
 
int ans, u0, d0, u1, d2;
 
void get(int o) {
    if(t[o].ma[0] < d0 || t[o].mi[0] > u0 || t[o].mi[1] > u1 || t[o].ma[2] < d2 || t[o].mx < ans) return;
    if(t[o].mi[0] >= d0 && t[o].ma[0] <= u0 && t[o].ma[1] <= u1 && t[o].mi[2] >= d2) {
        ans = t[o].mx; return;
    }
    if(a[o].d[0] >= d0 && a[o].d[0] <= u0 && a[o].d[1] <= u1 && a[o].d[2] >= d2) cmax(ans, c[a[o].d[0]]);
    if(t[ls].mx > t[rs].mx) get(ls), get(rs); else get(rs), get(ls);
}
 
int main() {
    scanf("%d %d", &n, &m);
    fo(i, 1, n) {
        scanf("%d", &c[i]);
        a[i].d[0] = i;
        a[la[c[i]]].d[2] = i;
        a[i].d[1] = la[c[i]];
        la[c[i]] = i;
    }
    fo(i, 1, n) a[la[i]].d[2] = n + 1;
    bt(rt, 1, n);
    fo(ii, 1, m) {
        scanf("%d %d", &x, &y);
        x = (x + ans) % n + 1; y = (y + ans) % n + 1;
        if(x > y) swap(x, y);
        ans = 0;
        d0 = x; u0 = y; u1 = x - 1; d2 = y + 1;
        get(rt);
        printf("%d\n", ans);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值