【差分】Tallest Cow

Tallest Cow

题目

  • FarmerJohn 有n头牛,它们按顺序排成一列。 FarmerJohn 只知道 其中最高的奶牛的序号及它的高度,其他奶牛的高度都是未知的。现在 FarmerJohn 手上有RR条信息,每条信息上有两头奶牛的序号(aa和bb),其中bb奶牛的高度一定大于等于aa奶牛的高度,且aa,bb之间的所有奶牛的高度都比aa小。现在FarmerJohnFarmerJohn想让你根据这些信息求出每一头奶牛的可能的最大的高度。(数据保证有解)

输入

  • 第1行:四个以空格分隔的整数:nn,ii,hh和RR(nn和RR意义见题面; ii 和 hh 表示第 ii 头牛的高度为 hh ,他是最高的奶牛)
    接下来R行:两个不同的整数aa和bb(1 ≤ aa,bb ≤ n)

输出

  • 一共n行,表示每头奶牛的最大可能高度.

数据范围

  • 1 ≤ n ≤ 10000 ; 1 ≤ h ≤ 1000000 ; 0 ≤ R ≤ 10000)

输入样例

9 3 5 5
1 3
5 3
4 3
3 7
9 8

输出样例

5
4
5
3
4
4
5
5
5

解题思路

其实这道题就是一道差分,看每一头牛被多少对牛所包围

<think>题目:Balanced Lineup 问题描述:有N头牛,每头牛有一个身高。有Q个查询,每个查询给出一个区间[A, B],要求输出该区间内最高牛的身高和最低牛的身高之差。 由于N最大为50,000,Q最大为200,000,我们不可能对每个查询都遍历区间(那将是O(N*Q)的复杂度,会超时)。 因此,我们需要一种高效的数据结构来支持区间最大/最小值查询。线段树(Segment Tree)或稀疏表(Sparse Table)是合适的选择。 这里我们使用线段树,因为线段树支持更新操作(虽然本题没有更新,但线段树是一个通用解法),而且空间复杂度为O(N),构建时间为O(N),每个查询时间为O(logN)。 步骤: 1. 构建线段树,每个节点存储对应区间的最大值和最小值。 2. 对于每个查询,查询区间的最大值和最小值,然后输出差值。 线段树节点设计: struct Node { int l, r; int max_val, min_val; } 构建树:递归构建,每个节点的最大值和最小值由左右子树得到。 查询:对于给定的区间[A, B],返回一个包含最大值和最小值的结构,然后计算差值。 注意:数组下标从0开始,但题目输入中的牛编号是从1开始的,我们需要调整查询区间(减1)。 由于查询只有200,000次,每次查询O(logN)总时间为O(Q logN),在可接受范围内。 代码实现: 我们将实现一个线段树,支持: build_tree: 构建线段树 query: 查询区间的最小值和最大值 注意:线段树数组通常开4倍空间。 样例验证: 输入:N=6, Q=3, 高度=[1,7,3,4,2,5] 查询:[1,5] -> [0,4]:最大值7,最小值1 -> 6 [4,6] -> [3,5]:最大值5,最小值2 -> 3 [2,2] -> [1,1]:最大值7,最小值7 -> 0 实现: </think> ### 解决方案:使用线段树高效查询区间最值差 #### 问题分析 给定N头牛的身高序列和Q个区间查询,每个查询要求计算区间内最高身高和最低身高的差值。由于N最大为50,000,Q最大为200,000,暴力解法(每个查询遍历区间)的时间复杂度为O(NQ)会超时。 采用线段树可以在O(N)预处理时间和O(logN)单次查询时间内解决: 1. **线段树结构**:每个节点存储区间的最小值和最大值 2. **查询处理**:对每个区间查询,返回其最小值和最大值并计算差值 #### 完整代码实现(C++) ```cpp #include <iostream> #include <vector> #include <climits> #include <algorithm> using namespace std; struct Node { int min_val; int max_val; }; class SegmentTree { private: vector<Node> tree; vector<int> data; int n; void build_tree(int node, int start, int end) { if (start == end) { tree[node] = { data[start], data[start] }; } else { int mid = (start + end) / 2; build_tree(2 * node + 1, start, mid); // 左子树 build_tree(2 * node + 2, mid + 1, end); // 右子树 tree[node].min_val = min(tree[2 * node + 1].min_val, tree[2 * node + 2].min_val); tree[node].max_val = max(tree[2 * node + 1].max_val, tree[2 * node + 2].max_val); } } Node query_range(int node, int start, int end, int l, int r) { if (r < start || end < l) { return { INT_MAX, INT_MIN }; // 区间不重叠,返回无效值 } if (l <= start && end <= r) { return tree[node]; // 当前区间完全包含在查询区间内 } int mid = (start + end) / 2; Node left_res = query_range(2 * node + 1, start, mid, l, r); Node right_res = query_range(2 * node + 2, mid + 1, end, l, r); return { min(left_res.min_val, right_res.min_val), max(left_res.max_val, right_res.max_val) }; } public: SegmentTree(const vector<int>& arr) : data(arr), n(arr.size()) { tree.resize(4 * n); build_tree(0, 0, n-1); } int query(int l, int r) { Node res = query_range(0, 0, n-1, l, r); return res.max_val - res.min_val; } }; int main() { ios_base::sync_with_stdio(false); cin.tie(nullptr); int N, Q; cin >> N >> Q; vector<int> heights(N); for (int i = 0; i < N; i++) { cin >> heights[i]; } SegmentTree st(heights); for (int i = 0; i < Q; i++) { int A, B; cin >> A >> B; // 输入区间是从1开始,转换为0-based索引 [A-1, B-1] cout << st.query(A-1, B-1) << '\n'; } return 0; } ``` #### 代码详解 1. **线段树结构** - `Node`结构体:存储区间的最小值`min_val`和最大值`max_val` - 树数组`tree`:大小设为4倍原数组长度(确保完全二叉树存储) 2. **建树过程(O(N))** ```cpp void build_tree(int node, int start, int end) { if (start == end) { // 叶子节点 tree[node] = {data[start], data[start]}; } else { int mid = (start+end)/2; build_tree(2*node+1, start, mid); // 递归构建左子树 build_tree(2*node+2, mid+1, end); // 递归构建右子树 // 合并子树信息 tree[node].min_val = min(left.min, right.min); tree[node].max_val = max(left.max, right.max); } } ``` 3. **区间查询(O(logN))** ```cpp Node query_range(int node, int start, int end, int l, int r) { // 当前区间与查询区间无重叠 if (r < start || end < l) return {INT_MAX, INT_MIN}; // 当前区间完全包含在查询区间内 if (l <= start && end <= r) return tree[node]; // 部分重叠,递归查询子树 int mid = (start+end)/2; Node left = query_range(2*node+1, start, mid, l, r); Node right = query_range(2*node+2, mid+1, end, l, r); return { min(left.min_val, right.min_val), max(left.max_val, right.max_val) }; } ``` 4. **主流程** - 读取牛的数量`N`和查询数量`Q` - 读取牛的身高数据 - 构建线段树(预处理) - 对每个查询`[A,B]`: - 转换为0-based索引`[A-1, B-1]` - 调用`query()`获取区间最值差并输出 #### 复杂度分析 - **时间复杂度**: - 建树:O(N) - 单次查询:O(logN) - 总时间:O(N + QlogN) ≈ 50,000 + 200,000×log₂(50,000) ≈ 3.6×10⁶ - **空间复杂度**:O(N)(4倍数组空间 ≈ 200,000) #### 优化讨论 1. **稀疏表替代方案**: - 适用场景:静态数组(无更新操作) - 优势:O(1)查询,O(NlogN)预处理 - 劣势:空间复杂度O(NlogN) 2. **线段树优化**: - 非递归实现 - 内存池分配 - 查询剪枝优化 样例验证: ```text 输入: 6 3 1 7 3 4 2 5 1 5 4 6 2 2 输出: 6 // [1,5]: max=7, min=1 → 6 3 // [4,6]: max=5, min=2 → 3 0 // [2,2]: max=min=7 → 0 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值