题目链接:传送门
题意:给你n个人,知道每个人的身高和每个人的前面或者后面有多少个比他高的,让你输出字典序最小的可能的排序。
思路:我们可以将人按身高升序顺序,然后模拟插空,因为这样我们就能保证之后插入的人有位置可插,只要注意每次我们可以判断需要插入的空位置=min(k,n-i-k)。(i为第i个人,k为第i个人有k个比他高的),如果小于0(代表n-i-k<0,空位置小于k个),则就是不可能的,然后只要二分去找位置,用树状数组更新就行了。
附上代码:
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<iostream>
#include<map>
#include<vector>
#include<set>
#include<queue>
using namespace std;
const int inf = 0x3f3f3f3f;
typedef long long ll;
struct inst {
int num;
int v;
};
inst ax[100010];
int bn[100010];
int gg[200020];
int n;
int cmp(inst a, inst b) {
if (a.num == b.num) return a.v < b.v;
else return a.num < b.num;
}
int change(int pos, int v)
{
for (int i = pos; i <= n; i += i&(-i))
{
gg[i] += v;
}
return 0;
}
int qusum(int x)
{
int ans = 0;
for (int i = x; i>0; i -= i&(-i))
{
ans += gg[i];
}
return ans;
}
int main(void) {
int t;
scanf("%d", &t);
int zzz = 1;
while (t--) {
int ok = 1;
scanf("%d", &n);
memset(gg, 0, sizeof(gg));
for (int i = 1; i <= n; i++) {
scanf("%d%d", &ax[i].num, &ax[i].v);
}
sort(ax + 1, ax + 1 + n, cmp);
for (int i = 1; i <= n; i++) {
int k = min(ax[i].v, n - i - ax[i].v);
if (k < 0) {
ok = 0;
break;
}
int l = 1;
int r = n;
int mid;
int aaa = 0;;
k++;
while (l <= r) {
mid = (l + r) >> 1;
if (mid - qusum(mid) >= k) {
r = mid - 1;
aaa = mid;
}
else l = mid + 1;
}
change(aaa, 1);
bn[aaa] = ax[i].num;
}
printf("Case #%d:", zzz++);
if (ok) {
for (int i = 1; i <= n; i++) {
printf(" %d", bn[i]);
}
printf("\n");
}
else printf(" impossible\n");
}
return 0;
}