算法竞赛进阶指南---0x42(树状数组)Lost Cows

题面

在这里插入图片描述

题解

  1. 先看题中样例,我们对于给定的数据,可以从后往前推,一开始牛的高度集合{1,2,3,4,5}(每个高度只能用一次),对于第5个牛给定的数据是0,说明它前面没有比它矮的牛,它的高度就是1,剩余集合{2,3,4,5},对于第4个牛给定的数据是1,说明在剩下的集合中只有1个牛比它矮,那就是第二小,说明它的高度就是3,以此类推,就可以算出每个牛的高度,所以我们可以发现,对于每一个牛ai,我们只需要在剩余的集合中找到一个排名为第 ai+1 的数,就是牛的高度
  1. 那么我们现在的问题就转化成为了两个问题:从剩余的数中找出第k个最小的数和删除某个数,这样我们可以用很多方法,比如平衡树,但是比较麻烦,我们还是用树状数组来做。继续转化,我们用数组下标表示每个高度,初始化为1,表示每个高度只能用一次,这样删除某个数就会变成add(x,-1)对于操作1我们可以用二分
  1. 因为我们的sum求的是前缀和,那么它一定是一个递增的序列,从剩余的数中找出第k个最小的数就相当于二分找一个最小的x,使得sum(x)=k(因为序列初始化都是1,删除就变成了0,所以前缀和的值就是当前的排名)

代码

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>

using namespace std;
const int N = 1e5 + 10;
int n;
int h[N];
int tr[N];
int res[N];

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

void add(int x, int c) {
    
    
    for (int i = x; i <= n; i += lowbit(i)) {
    
    
        tr[i] += c;
    }
}

int sum(int x) {
    
    
    int res = 0;
    for (int i = x; i; i -= lowbit(i)) {
    
    
        res += tr[i];
    }
    return res;
}

int main() {
    
    

    cin >> n;
    for (int i = 2; i <= n; i++) scanf("%d", &h[i]);

    //初始化每个区间都为1(优化:每个区间的值其实就是长度)
    for (int i = 1; i <= n; i++) tr[i] = lowbit(i);

    for (int i = n; i >= 1; i--) {
    
    
        int k = h[i] + 1;
        int l = 1, r = n;
        while (l < r) {
    
    
            int mid = (l + r) >> 1;
            if (sum(mid) >= k) r = mid;
            else l = mid + 1;
        }
        res[i] = l;
        add(l, -1);
    }
    for (int i = 1; i <= n; i++) cout << res[i] << endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_44791484/article/details/113836784