洛谷

洛谷P4148 简单题 题解 KD Tree + 替罪羊树

题目链接: https://www.luogu.com.cn/problem/P4148

题目大意:

两种操作:

平面内插入一个点;
查询一个矩形区域内所有点的权值和。
解题思路:

KD Tree模板题。

但是涉及插入操作,插入操作可能会导致树变得不平衡。

一棵子树如果不平衡,那怎么办?(\Rightarrow)

有没有一个支持重新建子树的平衡树,有。可以用 替罪羊树。

所以在 KD Tree 插入导致某棵子树不平衡时,将这棵子树以替罪羊树的方式 拍平 再 重建 即可。

示例程序:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;
const double alpha = 0.75;

struct Point {
    int dim[2], val;
} ord[maxn];    // 替罪羊树:用于保存拍平时的数据
int cnt;

struct Node {   // KD-Tree的结点
    int ls, rs;
    int mi[2], mx[2];   // mi[i]/mx[i]: 第i维的上下界
    int sum, sz;
    Point p;

    Node() {}
    Node(Point _p) {
        ls = rs = 0;
        for (int i = 0; i < 2; i++)
            mi[i] = mx[i] = _p.dim[i];
        sum = _p.val;
        sz = 1;
        p = _p;
    }

} tr[maxn];

stack<int> stk; // 保存释放掉的结点

int now, root, idx;

bool cmp(Point a, Point b) {
    return a.dim[now] < b.dim[now];
}

void push_up(int u) {
    int ls = tr[u].ls, rs = tr[u].rs;
    for (int i = 0; i < 2; i++) {
        tr[u].mi[i] = tr[u].mx[i] = tr[u].p.dim[i];
        if (ls) {
            tr[u].mi[i] = min(tr[u].mi[i], tr[ls].mi[i]);
            tr[u].mx[i] = max(tr[u].mx[i], tr[ls].mx[i]);
        }
        if (rs) {
            tr[u].mi[i] = min(tr[u].mi[i], tr[rs].mi[i]);
            tr[u].mx[i] = max(tr[u].mx[i], tr[rs].mx[i]);
        }
    }
    tr[u].sum = tr[u].p.val + tr[ls].sum + tr[rs].sum;
    tr[u].sz = 1 + tr[ls].sz + tr[rs].sz;
}

void slap(int u) {  // 替罪羊树:拍平
    if (!u) return;
    slap(tr[u].ls);
    ord[++cnt] = tr[u].p;
    stk.push(u);
    slap(tr[u].rs);
}

int build(int l, int r, int d) {   // 替罪羊树:建树
    if (l > r) return 0;
    int u;
    if (!stk.empty()) {
        u = stk.top();
        stk.pop();
    }
    else
        u = ++idx;
    int mid = (l + r) / 2;
    now = d;
    nth_element(ord+l, ord+mid, ord+r+1, cmp);
    tr[u].p = ord[mid];
    tr[u].ls = build(l, mid-1, d^1);
    tr[u].rs = build(mid+1, r, d^1);
    push_up(u);
    return u;
}

bool check_not_balance(int u) { // 替罪羊树:判断以u为根的子树是否不平衡
    int ls = tr[u].ls, rs = tr[u].rs;
    return max(tr[ls].sz, tr[rs].sz) > tr[u].sz * alpha;
}

void ins(int &u, Point p, int d) {
    if (!u) {
        if (!stk.empty()) {
            u = stk.top();
            stk.pop();
        }
        else
            u = ++idx;
        tr[u] = Node(p);
        return;
    }
    if (p.dim[d] < tr[u].p.dim[d])
        ins(tr[u].ls, p, d^1);
    else
        ins(tr[u].rs, p, d^1);
    push_up(u);
    if (check_not_balance(u)) {
        cnt = 0;
        slap(u);
        assert(cnt == tr[u].sz);
        u = build(1, cnt, d);
    }
}

int query(int u, int x1, int y1, int x2, int y2) {
    if (!u) return 0;
    int X1 = tr[u].mi[0], Y1 = tr[u].mi[1], X2 = tr[u].mx[0], Y2 = tr[u].mx[1];
    if (x1 <= X1 && X2 <= x2 && y1 <= Y1 && Y2 <= y2)
        return tr[u].sum;
    if (X2 < x1 || X1 > x2 || Y2 < y1 || Y1 > y2)
        return 0;
    int res = 0;
    int X = tr[u].p.dim[0], Y = tr[u].p.dim[1];
    if (x1 <= X && X <= x2 && y1 <= Y && Y <= y2)
        res += tr[u].p.val;
    res += query(tr[u].ls, x1, y1, x2, y2) + query(tr[u].rs, x1, y1, x2, y2);
    return res;
}

int n, op, ans;

int main() {
    scanf("%d", &n);
    while (~scanf("%d", &op)) {
        if (op == 1) {
            int x, y, val;
            scanf("%d%d%d", &x, &y, &val);
            x ^= ans;
            y ^= ans;
            val ^= ans;
            ins(root, Point{x, y, val}, 0);
        }
        else if (op == 2) {
            int x1, y1, x2, y2;
            scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
            x1 ^= ans;
            y1 ^= ans;
            x2 ^= ans;
            y2 ^= ans;
            ans = query(root, x1, y1, x2, y2);
            printf("%d\n", ans);
        }
        else // op == 3
            break;
    }
    return 0;
}

洛谷P4148 简单题 题解 KD Tree + 替罪羊树