HDU 5493 Queue 二分外加树状数组

题意:

有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;
}

猜你喜欢

转载自blog.csdn.net/weixin_43311695/article/details/108563823