Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Efficient Techniques for 2D Point Counting

Tech 1

Given $n$ points $(x_i, y_i)$ and $m$ rectangles, determine the number of points inside each rectangle.

Solution Approach

The problem requires counting points satisfying $l_j \le x_i \le r_j$ and $d_j \le y_i \le u_j$. By applying inclusion-exclusion, the count for a rectangle can be expressed as:

$$ \begin{aligned} \text{count} = &, Q(r_j, u_j) \ &- Q(l_j-1, u_j) \ &- Q(r_j, d_j-1) \ &+ Q(l_j-1, d_j-1) \end{aligned} $$

where $Q(a, b)$ counts points with $x_i \le a$ and $y_i \le b$.

To compute this offline, process events in increasing order of $y$-coordinate. For each point $(x_i, y_i)$, generate an insertion event at $y_i$. For each query boundary, generate a query event at the respective $y$-coordinate. Use a Fenwick tree to manage $x$-coordinates, updating and querying prefix sums.

Implementation

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int MAXN = 500010;

struct Point { int x_coord, y_coord; };

struct Event {
    int y_val, x_val, multiplier, query_id;
    bool operator<(const Event& other) const { return y_val < other.y_val; }
};

class FenwTree {
    vector<int> tree;
    int size;
public:
    FenwTree(int n) : size(n), tree(n + 1, 0) {}
    void modify(int idx, int delta) {
        for (; idx <= size; idx += idx & -idx)
            tree[idx] += delta;
    }
    int prefix_sum(int idx) {
        int sum = 0;
        for (; idx > 0; idx -= idx & -idx)
            sum += tree[idx];
        return sum;
    }
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int num_points, num_queries;
    cin >> num_points >> num_queries;
    vector<Point> points(num_points);
    vector<int> x_vals;
    for (int i = 0; i < num_points; i++) {
        cin >> points[i].x_coord >> points[i].y_coord;
        x_vals.push_back(points[i].x_coord);
    }

    vector<Event> events;
    for (int i = 0; i < num_points; i++)
        events.push_back({points[i].y_coord, points[i].x_coord, 0, -1});

    for (int i = 0; i < num_queries; i++) {
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        events.push_back({y2, x2, 1, i});
        events.push_back({y2, x1 - 1, -1, i});
        events.push_back({y1 - 1, x2, -1, i});
        events.push_back({y1 - 1, x1 - 1, 1, i});
    }

    sort(x_vals.begin(), x_vals.end());
    x_vals.erase(unique(x_vals.begin(), x_vals.end()), x_vals.end());
    int distinct_count = x_vals.size();

    auto get_index = [&](int val) {
        return lower_bound(x_vals.begin(), x_vals.end(), val) - x_vals.begin() + 1;
    };

    sort(events.begin(), events.end());
    FenwTree fenw(distinct_count);
    vector<int> results(num_queries, 0);

    for (auto& evt : events) {
        if (evt.multiplier == 0) {
            int pos = get_index(evt.x_val);
            fenw.modify(pos, 1);
        } else {
            int pos = upper_bound(x_vals.begin(), x_vals.end(), evt.x_val) - x_vals.begin();
            int res = fenw.prefix_sum(pos);
            results[evt.query_id] += evt.multiplier * res;
        }
    }

    for (int i = 0; i < num_queries; i++)
        cout << results[i] << '\n';

    return 0;
}

Application: Sum of Distinct Values in Intervals

Consider an array $a$ of length $n$. For each query $[l, r]$, compute the sum of distinct values in the subarray. Define $prev[i]$ as the last occurrence index of $a_i$ (or 0 if none). The contribution of $a_i$ is counted if $prev[i] < l$ and $l \le i \le r$. This transforms into a 2D point counting problem: points $(i, prev[i])$ and queries $(l \le i \le r, prev[i] < l)$.

Implementation

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int MAXN = 500010;

struct Event {
    int key, value, id, type;
    bool operator<(const Event& other) const { return key < other.key; }
};

class FenwTree {
    vector<ll> tree;
    int size;
public:
    FenwTree(int n) : size(n), tree(n + 1, 0) {}
    void adjust(int idx, ll delta) {
        for (; idx <= size; idx += idx & -idx)
            tree[idx] += delta;
    }
    ll range_sum(int idx) {
        ll sum = 0;
        for (; idx > 0; idx -= idx & -idx)
            sum += tree[idx];
        return sum;
    }
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int arr_size, query_count;
    cin >> arr_size >> query_count;
    vector<int> arr(arr_size + 1), prev_occur(arr_size + 1, 0);
    vector<int> last_pos(1000001, 0);

    for (int i = 1; i <= arr_size; i++) {
        cin >> arr[i];
        prev_occur[i] = last_pos[arr[i]];
        last_pos[arr[i]] = i;
    }

    vector<Event> events;
    for (int i = 1; i <= arr_size; i++)
        events.push_back({prev_occur[i], arr[i], i, 0});

    for (int i = 0; i < query_count; i++) {
        int left, right;
        cin >> left >> right;
        events.push_back({right, 0, i, 1});
        events.push_back({left - 1, 0, i, -1});
    }

    sort(events.begin(), events.end());
    FenwTree fenw(arr_size);
    vector<ll> answers(query_count, 0);

    for (auto& evt : events) {
        if (evt.type == 0) {
            fenw.adjust(evt.id, evt.value);
        } else {
            ll res = fenw.range_sum(evt.key);
            answers[evt.id] += evt.type * res;
        }
    }

    for (int i = 0; i < query_count; i++)
        cout << answers[i] << '\n';

    return 0;
}

Application: Range Minimum Query

Find the minimum value in subarray $[l, r]$. Represent each element $a_i$ as point $(i, a_i)$. Process events sorted by value, using a segment tree to track minimum values in active ranges.

Implementation

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 200010;
const int INF = 0x3f3f3f3f;

struct Event {
    int key, type, pos, r, id;
    bool operator<(const Event& other) const { return key < other.key; }
};

class SegTree {
    vector<int> min_val;
    int n;
    void build(int node, int l, int r) {
        if (l == r) {
            min_val[node] = INF;
            return;
        }
        int mid = (l + r) >> 1;
        build(node << 1, l, mid);
        build(node << 1 | 1, mid + 1, r);
        min_val[node] = min(min_val[node << 1], min_val[node << 1 | 1]);
    }
    void update(int node, int l, int r, int idx, int val) {
        if (l == r) {
            min_val[node] = val;
            return;
        }
        int mid = (l + r) >> 1;
        if (idx <= mid) update(node << 1, l, mid, idx, val);
        else update(node << 1 | 1, mid + 1, r, idx, val);
        min_val[node] = min(min_val[node << 1], min_val[node << 1 | 1]);
    }
    int query(int node, int l, int r, int ql, int qr) {
        if (ql > r || qr < l) return INF;
        if (ql <= l && r <= qr) return min_val[node];
        int mid = (l + r) >> 1;
        return min(query(node << 1, l, mid, ql, qr),
                   query(node << 1 | 1, mid + 1, r, ql, qr));
    }
public:
    SegTree(int sz) : n(sz) {
        min_val.resize(4 * sz);
        build(1, 1, n);
    }
    void assign(int idx, int val) { update(1, 1, n, idx, val); }
    int range_min(int l, int r) { return query(1, 1, n, l, r); }
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int arr_size, query_count;
    cin >> arr_size >> query_count;
    vector<int> arr(arr_size + 1);
    vector<Event> events;

    for (int i = 1; i <= arr_size; i++) {
        cin >> arr[i];
        events.push_back({arr[i], 0, i, 0, 0});
    }

    for (int i = 1; i <= query_count; i++) {
        int l, r;
        cin >> l >> r;
        events.push_back({0, 1, l, r, i});
    }

    sort(events.begin(), events.end());
    SegTree seg_tree(arr_size);
    vector<int> answers(query_count + 1, -1);

    for (auto& evt : events) {
        if (evt.type == 0) {
            seg_tree.assign(evt.pos, evt.key);
        } else {
            int res = seg_tree.range_min(evt.pos, evt.r);
            answers[evt.id] = (res == INF) ? -1 : res;
        }
    }

    for (int i = 1; i <= query_count; i++)
        cout << answers[i] << '\n';

    return 0;
}

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.