2 条题解
-
0
下面是一份“扫一遍+树状数组”解法的思路导图,假设你已经熟悉了 Fenwick Tree(树状数组)的「点增/前缀和查」操作:
一、把“左下”转成「一维前缀」+「扫描」
-
题中定义:
每颗星 的等级 = “落在它左下方的星数”, 即所有满足
xj ≤ xi, yj ≤ yi
的点的个数(不包括自己)。
-
二维到一维:
- 我们可以先按 从小到大走一遍(输入已经按此顺序给好:先按 升序,同 再按 升序)。
- 当我们处理到第 颗星时,前面所有已处理的点都自然满足 。
- 只剩下 “?” 这一维的判断——这正是一维前缀的问题。
二、用树状数组做「-前缀和」
- 我们建立一个 Fenwick Tree
bit[],下标对应 横坐标 (注意把它偏移到 1…MAX 范围内)。 - 含义:当一颗星被处理(“扫过”)后,就在它的 -位置做一个
update(x, +1),表示“该横坐标再也少了一颗星”。 - 查询:要知道有多少已扫过的星的 ,直接
qry(x_i)就能在 内给出答案。
三、完整流程
-
读入 颗星的 ,注意输入保证“按 升序,同 再按 升序”。
-
初始化:
bit[]全 0,cnt[0..N-1]全 0。 -
按输入顺序 遍历每一颗星 :
int k = qry(x + 1); // 先查有多少前面星的 x_j ≤ x cnt[k]++; // 它就是 k 级 upd(x + 1, +1); // 然后把自己插入 BIT,以便后面点能查到 -
最后按
k=0…N-1输出cnt[k]即可。
四、为什么一定正确?
- 不漏:因为我们只“扫”一次,每个点入队时前面的所有点恰好是 “” 的全集。
- 不断:每次
upd(x,+1)保证后面所有查询都能看到这颗星。 - 不重:因输入同 不重复、且每颗星只处理一次。
两条原则——扫描维 用 ,查询维 用 Fenwick Tree 管理的 前缀——完美契合题意“左下”区域的定义。
-
-
0
#include <bits/stdc++.h> using namespace std; const int MAXX = 32005; // x 坐标上限 + 安全余量 const int MAXN = 15005; // 星星数量上限 + 安全余量 int N; // 实际星星数 int bit[MAXX]; // 树状数组,用于统计 x ≤ 某值 的点数 int cnt[MAXN]; // cnt[k] = k 级星星的个数 int xs[MAXN], ys[MAXN]; // 存放每颗星的 x, y 坐标 // lowbit:取最低位的 1 inline int lowbit(int x) { return x & -x; } // 在树状数组 bit 上,对下标 i 加 val(本题 val 恒为 1) void upd(int i, int val) { for (; i < MAXX; i += lowbit(i)) { bit[i] += val; } } // 查询 bit 的前缀和,即 sum(bit[1..i]) int qry(int i) { int s = 0; for (; i > 0; i -= lowbit(i)) { s += bit[i]; } return s; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cin >> N; for (int i = 0; i < N; i++) { cin >> xs[i] >> ys[i]; // 输入保证按 y 升序,同 y 时按 x 升序,无需再排序 } // 初始化 memset(bit, 0, sizeof(bit)); memset(cnt, 0, sizeof(cnt)); // 按输入顺序“扫”每颗星 for (int i = 0; i < N; i++) { int x = xs[i] + 1; // 把 x 从 [0..32000] 映射到 [1..32001] // 查询有多少星的 x_j ≤ x 且 y_j ≤ 当前 y(因为已按 y 排序) int k = qry(x); // k 就是当前星的“级别” cnt[k]++; // 把自己插入结构,后续星可统计到它 upd(x, 1); } // 输出各级星星的数量:0 级到 N−1 级 for (int k = 0; k < N; k++) { cout << cnt[k] << "\n"; } return 0; }
- 1
信息
- ID
- 992
- 时间
- 250ms
- 内存
- 64MiB
- 难度
- 9
- 标签
- 递交数
- 27
- 已通过
- 3
- 上传者