分析
从后往前考虑
- 从剩余数中找第 k=a[i]+1 小数即为当前牛身高
- 删除找到的数
- 重复 1, 2
暴力超时,平衡树直接支持这两种操作,但太麻烦。用 h[i]=1 表示身高 i 未被使用,通过树状数组维护 h 的前缀和,表示 [1,x] 内还有多少身高可用
- 删除某个数,直接令 hi−=1
- 找剩余数中第 k 小,等价于找最小的 x,使 sum(x)=∑xi=1h[i]=k,x 增加时,sum(x) 单调不递减,可二分
时间复杂度 O(nlog2n)<3×107, n≤105
#include <cstdio>
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int n;
int a[N];
int ans[N];
int tr[N];
int lowbit(int x) {
return x & -x;
}
void init() {
for (int i = 1; i <= n; i ++) {
tr[i] += 1;
int j = i + lowbit(i);
if (j <= n) tr[j] += tr[i];
}
}
void add(int x, int c) {
for (int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}
int sum(int x) {
int s = 0;
for (int i = x; i; i -= lowbit(i)) s += tr[i];
return s;
}
int main() {
scanf("%d", &n);
for (int i = 2; i <= n; i ++) scanf("%d", a + i);
init();
for (int i = n; i; i --) {
int l = 1, r = n, k = a[i] + 1;
while (l < r) {
int mid = l + r >> 1;
if (sum(mid) >= k) r = mid;
else l = mid + 1;
}
ans[i] = l;
add(l, -1);
}
for (int i = 1; i <= n; i ++) printf("%d\n", ans[i]);
return 0;
}
大佬的题解写的太棒啦!
我是蒟蒻,题解全是重复y总的话
但是大佬总结的非常棒啊!
题解的排版和 LATEX 也非常美观