题意,一共有 N 个人,每个人的身高都不同, 知道其中每个人的高度 h 以及在他前面或者后面有 k 个人比他高, 问原先的队列, 如果有多种站法, 求按身高字典序最小的。
做法,因为每个人的身高不同, 所以我们先将问题简化, 假设 N 个人的身高分别是 1, 2, 3 ... N 。
然后我们倒过来将人插到原先站好的序列中还原原序列。假设当前还原到身高为 N - 2 的这个人,已知站在他前面或者后面的人有 2 个。那么在这之前我们处理好的人有 N - 1 和 N, 因为这两个人都比 N - 2 高, 所以这两个人怎么站的对于 N - 2 来说没有影响。同理, N - 2 站的位置相对于之前两人对于 1 ~ N - 3 这些人来说也没有影响。 因为无论如何他们三人总是最高的三人。 那么做法就比较明确了, 倒过来还原序列, 第 i 个处理到的人插到当前序列的 min(k, i - 1 - k ) 的位置中。
因为序列是不断扩张的, 所以它们的数组下标也不断在变化, 这样寻找当前序列的位置就有些困难, 那么可以顺着用一个树状数组 + 二进制前缀和的方式维护当前位置 pos 在没处理的位置中的位置。
参考代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int n, s[N], T[N];
int lowbit(int x) {
return x & -x;
}
void add(int x) {
for (; x <= n; x += lowbit(x)) T[x]++;
}
void sub(int x) {
for (; x <= n; x += lowbit(x)) T[x]--;
}
int sum(int x) {
int ans = 0;
for (; x; x -= lowbit(x)) ans += T[x];
return ans;
}
int get(int w, int l, int r) {
if (l == r) return l;
int mid = (l + r) >> 1;
if (sum(mid) >= w) return get(w, l, mid);
return get(w, mid + 1, r);
}
struct node {
int h, p;
node() {}
node(int h, int p) : h(h), p(p) {}
bool operator < (const node & a) const {
return h < a.h;
}
void read() {
scanf("%d %d", &h, &p);
}
} a[N];
int CASE, b[N];
int main() {
int t;
scanf("%d", &t);
while (t--) {
memset(T, 0, sizeof T);
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
add(i);
a[i].read();
}
sort(a + 1, a + n + 1);
int FLAG = 1;
for (int i = 1; i <= n; i++) {
int taller = n - i;
if (a[i].p > taller) {
FLAG = 0;
break;
}
int pos = min(a[i].p, taller - a[i].p) + 1;
int p = get(pos, 1, n);
b[p] = a[i].h;
sub(p);
}
printf("Case #%d:", ++CASE);
if (!FLAG) {
puts(" impossible");
continue;
}
for (int i = 1; i <= n; i++) printf(" %d", b[i]);
puts("");
}
}