题目地址:
https://www.acwing.com/problem/content/245/
有 n n n头奶牛,已知它们的身高为 1 ∼ n 1∼n 1∼n且各不相同,但不知道每头奶牛的具体身高。现在这 n n n头奶牛站成一列,已知第 i i i头牛前面有 A i A_i Ai头牛比它低,求每头奶牛的身高。
输入格式:
第
1
1
1行:输入整数
n
n
n。
第
2
,
.
.
.
,
n
2,...,n
2,...,n行:每行输入一个整数
A
i
A_i
Ai,第
i
i
i行表示第
i
i
i头牛前面有
A
i
A_i
Ai头牛比它低。(注意:因为第
1
1
1头牛前面没有牛,所以并没有将它列出)
输出格式:
输出包含
n
n
n行,每行输出一个整数表示牛的身高。第
i
i
i行输出第
i
i
i头牛的身高。
数据范围:
1
≤
n
≤
1
0
5
1≤n≤10^5
1≤n≤105
考虑数组 C = [ 1 , 2 , . . . , n ] C=[1,2,...,n] C=[1,2,...,n],接着对 A A A数组从后向前遍历,对 A [ n ] A[n] A[n],第 n n n头牛的高度一定是 C C C里排名第 A [ n ] + 1 A[n]+1 A[n]+1的数,接着将该数删掉( C C C别的数保持原序);然后看 A [ n − 1 ] A[n-1] A[n−1],第 n − 1 n-1 n−1头牛的高度一定是 C C C里排名第 A [ n − 1 ] + 1 A[n-1]+1 A[n−1]+1的数;以此类推。可以用平衡树来做,也可以用树状数组 + 二分来做。考虑长 n n n的元素全是 1 1 1的数组 B B B,那么找第 k k k个数就是在找使得前缀和 p [ s ] p[s] p[s]大于等于 k k k的最小的 s s s,然后将 B [ s ] B[s] B[s]减为 0 0 0,以此类推。可以用树状数组维护 B B B数组,每次对前缀和进行二分答案即可。代码如下:
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int n, a[N], res[N];
int tr[N];
int lowbit(int x) {
return x & -x;
}
void add(int k, int x) {
for (int i = k; i <= n; i += lowbit(i)) tr[i] += x;
}
int sum(int k) {
int res = 0;
for (int i = k; 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]);
// B数组里每个数都是1,tr[i]存的和就是其维护的区间长度
for (int i = 1; i <= n; i++) tr[i] = lowbit(i);
for (int i = n; i; i--) {
int l = 1, r = n;
while (l < r) {
int mid = l + r >> 1;
if (sum(mid) >= a[i] + 1) r = mid;
else l = mid + 1;
}
res[i] = l;
add(l, -1);
}
for (int i = 1; i <= n; i++) printf("%d\n", res[i]);
return 0;
}
时间复杂度 O ( n log 2 n ) O(n\log^2n) O(nlog2n),空间 O ( n ) O(n) O(n)。