树状数组 - 谜一样的牛

树状数组 - 谜一样的牛

题目:

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

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

输入格式
第1行:输入整数n。

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

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

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

数据范围
1≤n≤105

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

题解:

2nnann1nannan+1nnnan+1 n1n1an1+1 k10[1,x]x kkk给定的第2到n头牛的信息,首先可以确定的就是最后一头的身高。\\假设第n头牛的信息是a_n,即前n-1头牛当中,比第n头牛高的共有a_n头,即第n头牛是第a_n+1高的,\\n头牛的身高分别是前n大的自然数,第n头牛的身高就是就是第a_n+1大的自然数。\\\ \\以此类推,第n-1头牛的身高就是剩下的n-1个自然数当中第a_{n-1}+1大的自然数。\\\ \\可见,我们每次都需要看还剩下多少自然数没有被选过,在剩下的这些数当中再选择第k大的。\\因此,可以用树状数组来统计剩下的自然数的个数。\\把每个自然数初始化标记为1,每选择一个自然数,就将其标记为0,\\求[1,x]的前缀和即求前x个自然数中剩下的自然数的个数。\\\ \\查找剩下的自然数中第k大的自然数可以二分,找到第一个前缀和等于k的位置,这个下标就是剩下的第k大的自然数。


代码:

#include <cstdio>

using namespace std;

const int N=1e5+10;

int n,a[N],tr[N],ans[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()
{
    scanf("%d",&n);
    for(int i=2;i<=n;i++) scanf("%d",&a[i]);
    
    for(int i=1;i<=n;i++) tr[i]=lowbit(i);   // 等价于add(i,1)
    
    for(int i=n;i;i--)
    {
        int k=a[i]+1;   ///第k小的数即身高
        int l=1,r=n;
        while(l<r)
        {
            int mid=l+r>>1;
            if(sum(mid)>=k) r=mid;
            else l=mid+1;
        }
        ans[i]=r;
        add(r,-1);
    }
    
    for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
    
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章