【动态整体kth 权值BIT】 谜一样的牛

传送门

题意

\(n\) 头奶牛,已知它们的身高为 \(1\sim n\) 且各不相同,但不知道每头奶牛的具体身高。现在这 \(n\) 头奶牛站成一列,已知第 \(i\) 头牛前面有 \(A_{i}\) 头牛比他低,求每头牛的高度

数据范围

\(1\leq n \leq 10^{5}\)

题解

最后一头牛前面如果有 \(A_{i}\) 头牛比他低,显然他的身高 \(H_{n}=A_{n}+1\),倒数第二头牛前面有 \(A_{n-1}\) 头比他低,那么:

  • \(A_{n-1}<A_{n}\),则他的身高 \(H_{n-1}=A_{n-1}+1\)
  • \(A_{n-1} >=A_{n}\),则他的身高 \(H_{n-1}=A_{n-1}+2\)

以此类推,前 \(k\) 头有 \(A_{k}\) 头比他低,那么他的身高 \(H_{k}\) 是数值 \(1\sim n\) 中第 \(A_{k}+1\) 小的没有在 \({H_{k+1},H_{k+2},\dots,H_{n} }\) 出现过的次数,即还未确定的牛的身高即 \(1\sim n\) 的一个排列,需要进行两个操作

  • 从剩余的数中找出第 \(k\) 小的数
  • 将某一个数字删除

维护序列 \(b\),值都是1,表示这个高度还未使用,用树状数组维护其前缀和,如果已经用了一个数,删除直接加之间单点 \(-1\) 即可
用树状数组维护 \(b\) 的前缀和,二分找到前缀和为 \(A_{k}+1\) 的位置然后加-1即可
单次操作\(O((logN)^2)\)

Code

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<n;i++)
#define per(i,a,n) for(int i=n-1;i>=a;i--)
const int N=1e5+10;
int n;
int a[N],c[N],h[N];
int lowbit(int x) {return x&(-x);}
void add(int x,int d){
    for(int i=x;i<=n;i+=lowbit(i))
        c[i]+=d;
}
int ask(int x){
    int res=0;
    for(int i=x;i;i-=lowbit(i))
        res+=c[i];
    return res;
}
int main(){
    scanf("%d",&n);
    rep(i,2,n+1) scanf("%d",&a[i]);
    rep(i,1,n+1) add(i,1);
    per(i,1,n+1) {
        int x=a[i]+1;
        int l=1,r=n;
        while(l<r){
            int mid=l+r>>1;
            if(ask(mid) >= x)
                r=mid;
            else l=mid+1;
        }
        h[i]=l;
        add(l,-1);
    }
    rep(i,1,n+1) printf("%d\n",h[i]);
}
posted @ 2020-05-20 22:03  Hyx'  阅读(167)  评论(0)    收藏  举报