返回

POJ 2352 解密:纵横捭阖,树状数组傲视群雄

前端

引言

算法世界浩瀚无垠,数据结构犹如璀璨群星,而树状数组则在其中熠熠生辉。作为一种高效的数据结构,树状数组以其简洁的思想和强大的功能著称。在处理一系列特定问题时,树状数组往往能发挥出令人惊叹的威力。

问题剖析:POJ 2352

今天,我们将目光投向 POJ 2352 这道经典难题。题目要求我们求解二维坐标中每个点的左下方(包括正下方与正左方,但不包括自己)有多少个点。乍一看,这似乎是一个繁琐而棘手的任务,但借助树状数组的智慧,我们将轻松破解这一难题。

树状数组:二分搜索的利刃

树状数组是一种基于二进制思想构建的数据结构,它将一个一维数组巧妙地转化为一棵完全二叉树。通过这种巧妙的转换,树状数组可以高效地执行区间查询和区间修改操作。

二分搜索:精准定位

二分搜索是一种高效的搜索算法,它利用数据的有序性,通过不断缩小搜索范围来快速找到目标元素。在 POJ 2352 中,我们将使用二分搜索来定位每个点的左下方区域。

算法实现:庖丁解牛

  1. 树状数组初始化: 首先,我们将二维坐标中的所有点按横坐标从小到大排序,并建立一个树状数组 BIT,用于存储每个点的纵坐标。

  2. 二分搜索左边界: 对于每个点 (x, y),我们使用二分搜索找到其左边界 L,即第一个纵坐标大于或等于 y 的点的横坐标。

  3. 二分搜索右边界: 接下来,我们继续使用二分搜索找到其右边界 R,即第一个纵坐标大于 y 的点的横坐标减 1。

  4. 查询区间和: 利用树状数组,我们可以高效地查询区间 [L, R] 中的点的纵坐标和。该和即为点 (x, y) 左下方的点的个数。

  5. 输出结果: 最后,我们将每个点的左下方点的个数输出即可。

代码示例:

const int MAXN = 1e5 + 5;

int BIT[MAXN];
int n, m;

int lowbit(int x) {
    return x & (-x);
}

void update(int x, int val) {
    while (x <= n) {
        BIT[x] += val;
        x += lowbit(x);
    }
}

int query(int x) {
    int sum = 0;
    while (x > 0) {
        sum += BIT[x];
        x -= lowbit(x);
    }
    return sum;
}

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        int x, y;
        cin >> x >> y;
        update(x, 1);
    }
    for (int i = 1; i <= m; i++) {
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        int ans = query(x2) - query(x1 - 1);
        cout << ans << endl;
    }
    return 0;
}

结语:算法之美

通过 POJ 2352 这道难题,我们领略到了树状数组的强大威力。算法之美就在于此,它能将看似繁杂的问题化繁为简,让我们以清晰的思路和优雅的代码解决难题。在算法的海洋中不断探索,我们不仅能收获知识,更能体会到解决问题的无穷乐趣。