什么是线段树,超详细讲解
思路
考虑以每个身高作为下标,维护每个身高的数量,很显然不同的身高最多有 2 × 1 0 5 2\times 10^5 2×105 个,考虑离散化,代码如下:
for (int i = 1; i <= n; i ++) {int x = a[i].v;a[i].v = lower_bound(b + 1, b + idx + 1, a[i].v) - b; // 二分查找该元素的位置,保存离散化vis[a[i].v] = x;
}
用线段树维护每种身高数量的和,线段树上二分即可,具体细节看代码。
细节
- 考虑到要求的是身高,直接记录一下身高 x x x 离散化前的数字即可(这一步建议大家使用 S T L STL STL 的
lower_bound
,不然很容易错)。 - 在线段树叶子结点记录下当前节点代表的身高是多少。
代码(重点是注释)
#include <bits/stdc++.h> // 包含所有标准库#define int long long // 使用宏定义将int重新定义为长整型,以支持大数操作
#define ls (x<<1) // 定义左子树的宏,表示节点x的左子节点索引
#define rs (x<<1|1) // 定义右子树的宏,表示节点x的右子节点索引
#define mid ((l+r)>>1) // 定义区间中点的宏,用于分割区间using namespace std;
const int N = 2e5 + 5; // 定义常量N为200005,表示数组最大长度// 定义结构体node,用于存储每个元素的值v,权重a和原始索引id
struct node {int v, a, id;
} a[N];int ans[N], b[N], tot; // 定义答案数组ans,去重后的值数组b,以及总数tot
int vis[N]; // 定义访问数组vis,用于记录每个值的原始值
int tr[N << 2], cnt[N << 2]; // 定义线段树数组tr和计数数组cnt// build函数用于构建线段树,初始化计数数组cnt
void build(int x, int l, int r) {if (l == r) {cnt[x] = l;return;}build(ls, l, mid);build(rs, mid + 1, r);
}// change函数用于更新线段树中的元素,将位置k的元素值增加v
void change(int x, int l, int r, int k, int v) {if (l == r) {tr[x] += v;return;}if (k <= mid) change(ls, l, mid, k, v);else change(rs, mid + 1, r, k, v);tr[x] = tr[ls] + tr[rs];
}// query函数用于查询当前线段树中的中位数对应的元素
int query(int x, int l, int r, int k) {if (l == r) {return vis[cnt[x]];}if (tr[ls] < k) return query(rs, mid + 1, r, k - tr[ls]);else return query(ls, l, mid, k);
}// main函数是程序的入口
signed main() {cin >> n; // 输入元素个数nfor (int i = 1; i <= n; i++) {int tmp1, tmp2;cin >> tmp1 >> tmp2; // 输入每个元素的值和权重a[i] = (node){tmp1, tmp2, i}; // 存储到结构体数组a中b[++tot] = a[i].v; // 将值存储到数组b中,用于去重和排序}// 对b数组进行排序和去重,得到唯一元素的有序数组sort(b + 1, b + tot + 1);int idx = unique(b + 1, b + tot + 1) - b - 1;// 更新a数组中的值v为其在去重后数组中的索引,并在vis数组中记录原始值for (int i = 1; i <= n; i++) {int x = a[i].v;a[i].v = lower_bound(b + 1, b + idx + 1, a[i].v) - b; // 二分查询并离散化,为后续找中位数做铺垫vis[a[i].v] = x;}// 构建线段树build(1, 1, n);int res = 0;// 遍历每个元素,更新线段树,并查询当前中位数对应的元素for (int i = 1; i <= n; i++) {change(1, 1, n, a[i].v, a[i].a);res += a[i].a;cout << query(1, 1, n, res / 2 + res % 2) << "\n"; // 输出查询结果}return 0;
}