POJ 2352 Stars【树状数组】

448 阅读2分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

题目链接:POJ 2352

题目大意:在二维坐标内给定 n 个点,求出每个点的左下方(包括正下方与正左方,但是不包括自己),有多少个其他的点。

题目分析:

我们注意到,输入的点的坐标是按照 yy 坐标从小到大输入的,所以后输入的点一定不在先输入的星星的左下方。因此,我们想要统计当前点的左下方有多少个点,只需要统计在这个点出现之前的点,是否满足对应的要求。同时注意到,由于 yy 坐标已经呈现了单调递增的形式,因此我们只需要考虑 xx 坐标即可。

显然,在 yy 坐标已经是单调递增之后,想要统计有多少个点在当前点的左下方,就是在统计,有多少个点的横坐标,比当前点小。

我们使用 lociloc_i 表示 xx 坐标 ii 处,有 lociloc_i 个星星。那么对于当前点,如果横坐标为 xx,显然答案就是:

i=0xloci\sum_{i=0}^{x}loc_i

我们可以使用树状数组来维护这个数组,最终的时间复杂度为 O(nlog2n)O(n\log_2n)

要注意的一个细节是,我们使用树状数组维护,不能出现下标为 00 的情况,但是此题中却存在,我们可以考虑手动 +1+1 的方式来避免这个问题。

此题中,每个点的坐标范围是 0<=x,y<=320000 <= x, y <= 32000。但是假如每个点的坐标范围更大,在 0<=x,y<=INT_MAX0 <= x, y <= INT\_MAX 该怎么办呢?

解决的基本思路不变,只不过我们在考虑 x,yx,y 坐标时思路扩展一下。尽管每个点的坐标数值很大,但是我们总共最多只有 1500015000 个点,有太多的坐标数值是冗余的,因此我们可以考虑使用离散化。

我们使用离散化后离线处理的方式:

(1)我们首先读取所有的数据,将 x,yx,y 进行离散化。 (2)重新按照原本的解题思路处理即可。

非离散化版本的参考代码如下:

#include <cstdio>
#define ll long long
using namespace std;

const ll N = 2e4 + 5;
const ll M = 4e4 + 5;

ll n, c[M], ans[N];

inline ll read() {
    ll x = 0; char ch = getchar(); bool f = 0;
    while (ch > '9' || ch < '0') {if (ch == '-') f = 1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + ch - '0'; ch = getchar();}
    return f ? -x : x;
}

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

void update(ll x) {
    while (x < M) {
        c[x]++;
        x += lowbit(x);
    }
}

ll getsum(ll x) {
    ll sum = 0;
    while (x) {
        sum += c[x];
        x -= lowbit(x);
    }
    return sum;
}

int main() {
    n = read();
    for (ll i = 1; i <= n; ++i) {
        ll x = read() + 1, y = read();
        ans[getsum(x)]++;
        update(x);
    }
    for (ll i = 0; i < n; ++i) {
        printf("%lld\n", ans[i]);
    }
    return 0;
}