AcWing:244. 谜一样的牛(树状数组 + 二分)

2019-08-27

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

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

输入格式

第1行:输入整数n。

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

输出格式

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

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

数据范围

1n1051≤n≤105

输入样例:
5
1
2
1
0
输出样例:
2
4
5
3
1

题解:本题为找牛题,因为牛只有n头,身高也是各不相同的,所以,我们就可以建立一个树状数组来记录当前牛身高的牛是否使用过(树状数组里的值表示的是,当前牛是所有未使用过的牛中的第几高),因为树状数组是默认按升序排的,所以现在我们就只需要逆序遍历那个每头牛前面有多少头牛比它矮的arr数组(其中当前这头牛本身的值是arr[i] + 1),然后我们就二分找到树状数组中第arr[i] + 1身高的牛,然后记录下来,在从树状数组中将此牛减去,重复以上操作n次即可。

 

#include <iostream>
#include <cstdio>

using namespace std;

const int maxn = 1e5+7;

int n;
int tree[maxn];    //01标记N头牛是否使用过
int arr[maxn];
int ans[maxn];

int lowbit(int x) {
    return x & (-x);
}

void add(int x, int val) {
    while(x <= n) {
        tree[x] += val;
        x += lowbit(x);
    }
}

int ask(int x) {
    int res = 0;
    while(x >= 1) {
        res += tree[x];
        x -= lowbit(x);
    }
    return res;
}

int main() {
    scanf("%d", &n);
    add(1, 1);    //把所有的牛都加入树状数组
    for(int i = 2; i <= n; i++) {
        scanf("%d", &arr[i]);
        add(i, 1);
    }
    for(int i = n; i >= 1; i--) {    //需要逆序遍历找牛
        int l = 1, r = n;
        while(l < r) {        //二分找按身高排序的第arr[i] + 1头牛(因为当前这头牛前面有arr[i]头牛比它矮,所以它自己就是第arr[i] + 1头牛)
            int mid = (l + r) >> 1;
            if(ask(mid) < arr[i] + 1) {
                l = mid + 1;
            } else {
                r = mid;
            }
        }
        ans[i] = r;
        add(r, -1);    //因为当前这头牛使用过了,所以就要减去(在树状数组中去掉标记)
    }
    for(int i = 1; i <= n; i++) {
        printf("%d\n", ans[i]);
    }
    return 0;
}