题目链接:https://www.acwing.com/problem/content/description/245/
题目:
有 n 头奶牛,已知它们的身高为 1∼n 且各不相同,但不知道每头奶牛的具体身高。
现在这 n 头奶牛站成一列,已知第 i 头牛前面有 Ai 头牛比它低,求每头奶牛的身高。
输入格式
第 1 行:输入整数 n。
第 2..n 行:每行输入一个整数 Ai,第 i 行表示第 i 头牛前面有 Ai 头牛比它低。
(注意:因为第 1 头牛前面没有牛,所以并没有将它列出)输出格式
输出包含 n 行,每行输出一个整数表示牛的身高。
第 i 行输出第 i头牛的身高。
数据范围
1≤n≤1e5
输入样例:
5 1 2 1 0
输出样例:
2 4 5 3 1
思路:
举例:
0 1 2 1 0
我们刚开始可以使用的值为:(1,2,3,4,5)
我们从右往左去解:
i == 5时,b[i] == 0, 所以可以知道,前 1~4没有比它小的值,所以它只能是当前剩下的里面的最小的值。
所以a[5] == 1;(而可用情况由,1,2,3,4,5 变为了 2,3,4,5)
i == 4时, b[i] == 1, 所以可以知道, 前1 ~ 3有一个比它小的值, 所以它只能是当前剩下的里面倒数第二小的值。
所以a[4] == 3;(而可用的情况,由,2,3,4,5变为了,2 ,4,5)
i == 3时, b[i] == 2, 所以可以知道,前1~2有两个比它小的值,所以它只能是当前剩下的里面倒数第三个小的值。
所以a[3] == 5;(而可用的情况,由2,4,5变为了2,4)
i == 2时,b[i] == 1,所以可以知道,前1有一个比它小的值,所以它只能是剩下的里面倒数第二个小的值
所以a[2] == 4,
a[1] == 2.
那么问题来了。如何实现呢?
我们可以使用树状数组的方式来维护我们现在还能够用的数值。
由于树状数组维护的是前缀和。 所以我们树状数组中的前缀和的含义就是 1~x中还有多少个数。
每一个数在树状数组中刚开始的时候都是1,
每使用掉一个值,那么树状数组中这个值就被-1. 使得前缀和 -1.
举例:我们要找当前剩下的数中第 k 小的数。
那么就可以使用二分的方式进行查询。
寻找树状数组中第一个 1~x区间 其和 大于等于 k。(也就是1~x中还剩下大于等于k个值,而又是第一个大于等于k的,其实就是第一个和为k的情况, 那么x的含义不就是当前1~n中剩下的第k小的数了嘛。)
所以可以通过树状数组 加 二分 的方式 实现。
代码实现:
# include <iostream>
using namespace std;
const int N = 2e5 + 10;
int h[N];
int a[N];
int c[N];
int n;
int lowbit(int x)
{
return x & -x;
}
void add(int x , int v)
{
for(int i = x ; i <= n ; i+=lowbit(i))
{
c[i] += v;
}
}
int sum(int x)
{
int res = 0;
for(int i = x ; i > 0 ; i -= lowbit(i))
{
res += c[i];
}
return res;
}
int main()
{
scanf("%d",&n);
h[1] = 0;
for(int i = 2 ; i <= n ; i++)
{
scanf("%d",&h[i]);
}
for(int i = 1 ; i <= n ; i++)
{
add(i,1); // 每个i值都可以用一次
}
for(int i = n ; i >= 1 ; i--)
{
int t = h[i] + 1 ; // i的前面有h[i]个比它小的,所以它是当前还剩下值的第h[i] + 1小的值
int l = 1 , r = n;
while(l < r) // 找第一个大于等于t的值
{
int mid = (l + r) / 2;
if(sum(mid) >= t)
{
r = mid;
}
else
{
l = mid + 1;
}
}
a[i] = l;
add(l,-1); // l被用掉了。所以减1.
}
for(int i = 1 ; i <= n ; i++)
{
printf("%d\n",a[i]);
}
return 0;
}
下面一道题目也可以用到树状数组的方式求解:
题目链接:https://ac.nowcoder.com/acm/contest/11253/K
题目题解:https://blog.csdn.net/qq_49120553/article/details/118940655?spm=1001.2014.3001.5501