有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
先說一下樹狀數組,樹狀數組是個很神奇的數據結構,定義tr[x]爲區間長度爲lowbit(x),末尾時x的區間和,就是[x-lowbit(x)+1,x]的區間和。
lowbit(x)=x&-x;
int lowbit(int x)
{
return x&-x;//返回二進制下最後一位1所對應的十進制,假設最後一位1是在第i位,就返回2^i
}
tr[x]是[x-lowbit(x)+1,x]的和,那麼如果想求[1,n]的和該怎麼求,tr[x-lowbit(x)]是不是剛好與tr[x]無縫相連,那麼一直這項拼接下去直到區間起點爲1就可以了(可以證明這樣是一定存在的)
int sum(int x)
{
int res=0;
for(int i=x;i;i-=lowbit(i)) res+=tr[i];
return res;
}
如果還想修改某個點的值那該怎麼做呢,假設要修改點x位置的值,只需要找一下有那麼些 tr[] 區間是包含點x的,tr[x]是一定得,然後得繼續向後找,
假設x二進制爲101011:
lowbit(x)=1,x+lowbit=101100,lowbit(x+lowbit)=100,
x+lowbit-lowbit(x+lowbit(x))=101000<x;
可以發現x+lowbit(x)會把x最後邊的連續1都進位,x+lowbit-lowbit(x+lowbit(x))就會一定小於x,所以tr[x+lowbit]一定會包含點x,一直令x+=lowbit(x),tr[x]都修改一下就行了。
void add(int x,int d)//第x個數加d
{
for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=d;
}
所以樹狀數組支持單點修改,區間求和,比線段樹作用單一但比線段樹代碼短
然後再看這道題,每頭牛隻知道它前面都幾頭牛比他低,如果ai=0,那麼i就是前i個的最小值,最後一個0呢就一定是1;ai=j,它就是前i個的第j+1小,如果前i個數已經確定那i就是第j和第j+1中間的數,那如果第j和第j+1之間沒有數了就錯誤了,所以不能先確定前面的數,只能先i確定後面的數,那麼前i個數就會有一個可能的集合,再這個集合裏面找到找第j+1小的數就是第i頭牛的高。
初始化的集合一定是1到n,令每個數都等1,已經確定過得高度就改爲0,所以第j+1小的點就是第一個前綴和等於j+1的點,就可以用樹狀數組啦
#include<iostream>
using namespace std;
typedef long long ll;
const int N=100010;
int n,a[N],tr[N];
int lowbit(int x)
{
return x&-x;
}
void add(int x,int d)
{
for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=d;
}
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++) cin>>a[i];
for(int i=1;i<=n;i++) add(i,1);
for(int i=n;i;i--)
{
int l=0,r=n,mid;
while(l<r)
{
mid=l+r+1>>1;
if(sum(mid)>a[i]) r=mid-1;
else l=mid;
}//l是小於等於ai的最大點
a[i]=l+1;
add(l+1,-1);//不能在第0個位置修改,能求和
}
for(int i=1;i<=n;i++) cout<<a[i]<<endl;
return 0;
}
再貼一道一模一樣的題:https://www.acwing.com/problem/content/262/