题目-谜一样的牛

问题分析

发现无法从前向后推断牛的身高

但是发现可以从后向前推断出当前牛的身高, 假设当前牛的身高是 w i w_i wi, 那么当前牛的身高是在剩余的数字中选择第
w i + 1 w_i + 1 wi+1
大的数字
因此可以将操作分为两类
- 从集合中删除某个数字
- 在剩余数字中找到第 w i + 1 w_i + 1 wi+1大的数字
上述的操作其实是平衡树的操作, 但是可以使用树状数组实现, 具体方法如下
使用权值树状数组, 初始化每个位置都是可用的, 支持单点删除, 查找序列中第 k k k个数字可以借助整数二分算法
因为树状数组的查询结果是 1 ∼ w + 1 1 \sim w + 1 1∼w+1之前有多少个数字是可用的, 根据二分就能当前位置是否是可用数字的第 w i + 1 w_i + 1 wi+1个位置
算法步骤
- 实现树状数组基本操作
- 实现二分查询
- 倒序计算当前位置的第 w i + 1 w_i + 1 wi+1大的数字
代码实现
这里二分的过程是 ≥ x \ge x ≥x, 因为求的是第 x x x大的数字, 如果是 ≤ \le ≤, 那么计算的是小于等于 x x x最大的数, 那就是错误的
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n, w[N];
int tr[N];
int lowbit(int x) {
return x & -x;
}
void modify(int u, int val) {
for (int i = u; i <= n; i += lowbit(i)) tr[i] += val;
}
int get(int u) {
int ans = 0;
for (int i = u; i; i -= lowbit(i)) ans += tr[i];
return ans;
}
int query(int x) {
int l = 1, r = n;
while (l < r) {
int mid = l + r >> 1;
if (get(mid) >= x) r = mid;
else l = mid + 1;
}
return l;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
for (int i = 1; i < n; ++i) cin >> w[i];
for (int i = 1; i <= n; ++i) modify(i, 1);
vector<int> ans;
for (int i = n - 1; i >= 0; --i) {
int k = query(w[i] + 1);
ans.push_back(k);
modify(k, -1);
}
reverse(ans.begin(),ans.end());
for (int i = 0; i < ans.size(); ++i) cout << ans[i] << '\n';
return 0;
}

京公网安备 11010502036488号