谜一样的牛(树状数组)

143 阅读2分钟

有 nn 头奶牛,已知它们的身高为 1n1∼n 且各不相同,但不知道每头奶牛的具体身高。

现在这 nn 头奶牛站成一列,已知第 ii 头牛前面有 AiA_i 头牛比它低,求每头奶牛的身高。

输入格式

第 11 行:输入整数 nn

第 2..n2..n 行:每行输入一个整数 AiA_i,第 ii 行表示第 ii 头牛前面有 AiA_i 头牛比它低。
(注意:因为第 11 头牛前面没有牛,所以并没有将它列出)

输出格式

输出包含 nn 行,每行输出一个整数表示牛的身高。

第 ii 行输出第 ii 头牛的身高。

数据范围

1n1051≤n≤10^5

输入样例:

5
1
2
1
0

输出样例:

2
4
5
3
1

题目分析

这是一道 二分加树状数组 的题目。

首先我们先分析题目信息,队列中的每头牛我们可以知道在这头牛之前有多少头牛比他矮,我们将其定义为 rk[i]rk[i],由于牛的身高为 1n1\sim n。 稍加思考,我们便可以立即想到队列中最后一头牛的身高,即 rk[n]+1rk[n]+1

那么,我们是否可以依次向前,进而求得所有牛的身高呢?答案是可以的。

这里便需要用到树状数组的知识,首先我们新开一个数组 f[i]f[i] 代表当前身高是否已经找到对应牛的位置,一开始初始化为 11。当我们知道一头相应位置牛的身高后,便将 f[i]f[i] 置零。

这时,我们便可用树状数组动态统计 f[i]f[i] 的前缀和了,从后向前,每次 rk[i]+1rk[i]+1 即对应相应位置前缀和,即当前牛的身高。

为了找到相应位置,我们可以二分数组长度,通过 f[i]f[i] 前缀和判断是否合法,最终得到相应身高。

最终复杂度为 O(nlognlogn)O(nlognlogn)

Accept代码

#include <bits/stdc++.h>

using namespace std;

const int N = 100010;

int n;
int h[N], f[N], rk[N];

void modify(int x, int k)
{
    for ( ; x <= n; x += x & -x) f[x] += k;
}

int query(int x)
{
    int res = 0;
    for ( ; x; x -= x & -x) res += f[x];
    return res;
}

int main()
{
    cin >> n;
    for (int i = 2; i <= n; i ++) cin >> rk[i];
    for (int i = 1; i <= n; i ++) modify(i, 1);
    for (int i = n; i; i --)
    {
        int x = rk[i];
        int l = 1, r = n;
        while (l < r)
        {
            int mid = l + r >> 1;
            if (query(mid) >= x + 1) r = mid;
            else l = mid + 1;
        }
        h[i] = r;
        modify(r, -1);
    }
    for (int i = 1; i <= n; i ++) cout << h[i] << "\n";
    return 0;
}