Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
题目链接:POJ 2352
题目大意:在二维坐标内给定 n 个点,求出每个点的左下方(包括正下方与正左方,但是不包括自己),有多少个其他的点。
题目分析:
我们注意到,输入的点的坐标是按照 坐标从小到大输入的,所以后输入的点一定不在先输入的星星的左下方。因此,我们想要统计当前点的左下方有多少个点,只需要统计在这个点出现之前的点,是否满足对应的要求。同时注意到,由于 坐标已经呈现了单调递增的形式,因此我们只需要考虑 坐标即可。
显然,在 坐标已经是单调递增之后,想要统计有多少个点在当前点的左下方,就是在统计,有多少个点的横坐标,比当前点小。
我们使用 表示 坐标 处,有 个星星。那么对于当前点,如果横坐标为 ,显然答案就是:
我们可以使用树状数组来维护这个数组,最终的时间复杂度为 。
要注意的一个细节是,我们使用树状数组维护,不能出现下标为 的情况,但是此题中却存在,我们可以考虑手动 的方式来避免这个问题。
此题中,每个点的坐标范围是 。但是假如每个点的坐标范围更大,在 该怎么办呢?
解决的基本思路不变,只不过我们在考虑 坐标时思路扩展一下。尽管每个点的坐标数值很大,但是我们总共最多只有 个点,有太多的坐标数值是冗余的,因此我们可以考虑使用离散化。
我们使用离散化后离线处理的方式:
(1)我们首先读取所有的数据,将 进行离散化。 (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;
}