题目-谜一样的牛

在这里插入图片描述

问题分析

在这里插入图片描述
发现无法从前向后推断牛的身高
在这里插入图片描述
但是发现可以从后向前推断出当前牛的身高, 假设当前牛的身高是 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 1w+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;
}