题意:
有N个人排成了一队,他们知道自己的身高以及前面(或者后面)有多少人比他们高,要求我们确定这N个人的排队顺序(如果有多种,输出字典序最小的那个)
思路:
我们首先将这N个人按照高度从小到大排序(当然从大到小也可以),遍历这N个人,依次为他们寻找可能的位置。因为题中给的K可能是前面也可能是后面,所以有两种情况,我们只需取最小的位置即可,这样才能保证最后的答案是最下的字典序。
那么位置该如何去找呢?(可以将问题抽象成N个萝卜放N个坑里面)一开始所有人的位置都还没找到,数组t[N]的值都为1,1表示这个坑还没有用,0表示已经用过了。假设 N=5,数组 t :1 0 1 1 0, 现在我们要找第三个人的位置,因为我们已经知道了K,我们就可以找左边区间1的个数大于K的最小位置(因为我们是按身高从小到大填坑的,所以剩下的身高一定是大于等于此时遍历到的身高,剩下的1( t数组中)无论怎么填身高都是大于等于当前身高的)。确定位置可以二分高效求得,而快速计算左边区间1的数量就可以用树状数组高效求得了。最终的复杂度是n*logn*logn
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long ul;
typedef unsigned long long ull;
#define pi acos(-1.0)
#define e exp(1.0)
#define pb push_back
#define mk make_pair
#define fir first
#define sec second
#define scf scanf
#define prf printf
typedef pair<ll,ll> pa;
const ll INF=0x3f3f3f3f3f3f3f3f;
const ll MAX_T=1e3+7;
const ll MAX_N=1e5+7;
ll T,N;
ll answer[MAX_N];
struct node{
//按照高度从小到大排序
ll h,sum,id;
bool operator<(const node &a)const {
return h<a.h;
}
}hei[MAX_N];
ll t[MAX_N],t1[MAX_N];
ll lowbit(ll x){
return x&(-x);
}
void add(ll *t,ll x,ll k){
for(;x<=N;x+=lowbit(x)){
t[x]+=k;
}
return ;
}
ll ask(ll *t,ll x){
ll res=0;
for(;x;x-=lowbit(x)){
res+=t[x];
}
return res;
}
ll Find_pos(ll *t,ll sum){
//二分找位置
ll i,j,L=1,R=N,mid,ans;
while(L<=R){
mid=L+(R-L)/2;
ll tmp=ask(t,mid);
if(tmp>=sum){
ans=mid;
R=mid-1;
}
else
L=mid+1;
}
return ans;
}
int main()
{
// freopen(".../.txt","w",stdout);
// freopen(".../.txt","r",stdin);
// ios::sync_with_stdio(false);
ll i,j,k=0;
scf("%lld",&T);
while(T--){
scf("%lld",&N);
memset(t,0,sizeof(t));
memset(t1,0,sizeof(t1));
memset(answer,0,sizeof(answer));
for(i=1;i<=N;i++){
scf("%lld %lld",&hei[i].h,&hei[i].sum);
hei[i].id=i;
hei[i].sum++;//便于后面位置的查找
}
sort(hei+1,hei+N+1);
bool flag=0;
for(i=1;i<=N;i++){
//判断不存在的情况
if(hei[i].sum>N-i+1){
//比他高的只有N-i+1人,而他却说有hei[i].sum个人比他高(hei[i].sum>N-i+1)
flag=1;
break;
}
}
if(flag){
prf("Case #%lld: impossible\n",++k);
continue;
}
for(i=1;i<=N;i++){
//初始化,起初位置都可放,用两个树状数组分别考虑前面后面两种情况
add(t,i,1);
add(t1,i,1);
}
for(i=1;i<=N;i++){
ll pos=Find_pos(t,hei[i].sum);
ll pos1=N-Find_pos(t1,hei[i].sum)+1;//因为用树状数组求左边区间好求,那怎么办求右边区间呢(对应后边的情况)?我们只要将数组反转一下,把右区间转换成左边区间
pos=min(pos,pos1);
answer[pos]=hei[i].h;
add(t,pos,-1);//去掉一个位置
add(t1,N-pos+1,-1);
}
prf("Case #%lld:",++k);
for(i=1;i<=N;i++)
prf(" %lld",answer[i]);
puts("");
}
return 0;
}