HDU 5493 Queue【树状数组+二分查找】

版权声明:转载什么的好说,附上友链就ojek了,同为咸鱼,一起学习。 https://blog.csdn.net/sodacoco/article/details/86500229

参看资料:

https://blog.csdn.net/sdz20172133/article/details/86497650

https://blog.csdn.net/sky_miange/article/details/48791881

https://blog.csdn.net/hyczms/article/details/48767933


题目:

NN people numbered from 1 to NN are waiting in a bank for service. They all stand in a queue, but the queue never moves. It is lunch time now, so they decide to go out and have lunch first. When they get back, they don’t remember the exact order of the queue. Fortunately, there are some clues that may help. 
Every person has a unique height, and we denote the height of the ii-th person as hihi. The ii-th person remembers that there were kiki people who stand before him and are taller than him. Ideally, this is enough to determine the original order of the queue uniquely. However, as they were waiting for too long, some of them get dizzy and counted kiki in a wrong direction. kiki could be either the number of taller people before or after the ii-th person. 
Can you help them to determine the original order of the queue? 

Input

The first line of input contains a number TT indicating the number of test cases (T≤1000T≤1000). 
Each test case starts with a line containing an integer NN indicating the number of people in the queue (1≤N≤1000001≤N≤100000). Each of the next NN lines consists of two integers hihi and kiki as described above (1≤hi≤109,0≤ki≤N−11≤hi≤109,0≤ki≤N−1). Note that the order of the given hihi and kiki is randomly shuffled. 
The sum of NN over all test cases will not exceed 106106 

Output

For each test case, output a single line consisting of “Case #X: S”. XX is the test case number starting from 1. SS is people’s heights in the restored queue, separated by spaces. The solution may not be unique, so you only need to output the smallest one in lexicographical order. If it is impossible to restore the queue, you should output “impossible” instead.

Sample Input

3
3
10 1
20 1
30 0
3
10 0
20 1
30 0
3
10 0
20 0
30 1

Sample Output

Case #1: 20 10 30
Case #2: 10 20 30
Case #3: impossible

题目大意:

        有n个人,对于第 i 个人,给定这个人的身高 hi ,这个人前面或者后面比他高的人数ki。求原来的队列顺序,如果有多个结果,使字典序最小。

解题思路:

        没什么思路,,万脸懵逼,,听同学讲完后,,线段树??什么???线段树我看了啊,,没什么关系啊,,老老实实看题解:

        恩,,看了一些题解,明白了线段树的作用【用树状数组亦可】,目的是维护空位数目前缀和,这个一会说。

        看解题方法:

        1》既然每个人是按照身高确定的位置,不妨将所有人按身高从小到大排序,这样,将人按身高从小到大插入已有队列。

        2》因为是按身高从小到大取出,所以如果当前是第 i 人,那么已有队列中的所有人一定比他矮,所以需要在其前面或者后面预留 Ki 个空位,等待后面比 i 高的人补上。那么,怎么确定是在其前面留空位还是后面留空位?       

        3》留空位的原则参看“使整体字典序最小”,即先来的人(矮一些的)尽量往前排;保证他的前面有ki个空位(空位不是位置),当前位置就是(i+Ki)【已有人数+预留空位】;现在整体还有n-i个比它高的人,使其后面有ki个比他高的,前面就有n-i-Ki个,即得保证他的前面有n-i-Ki个空位,当前位置就是(i+n-i-Ki)【已有人数+预留空位】;

        4》要使当前位置尽量靠前,那么就选择两种可能中位置较小的地方,min((i+Ki),(i+n-i-Ki)) ;即比较预留空位数,选择较小的那一方,即保证前面空位数p=min(ki,n-i-ki)。

        怎么寻找有p个空位数的位置呢,二分+树状数组。

        树状数组我们维护长度为n的01序列,1代表该位置有空位,0代表被占领。

        我们二分位置,sum(mid)就表示在mid之前有几个1,即多少空位。

实现代码:

#include<stdio.h>
#include<string>
#include<string.h>
#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long ll;
const int MAXN=200000+5;    //最大元素个数
int n;//元素个数
int c[MAXN],ans[MAXN];      //c[i]==A[i]+A[i-1]+...+A[i-lowbit(i)+1]

int lowbit(int i){return i&(-i);}   //返回i的二进制最右边1的值

int sum(int x){             //返回A[1]+...A[i]的和
    int sum = 0;
    while(x){
        sum += c[x];
        x -= lowbit(x);
    }
    return sum;
}

void add(int x, int val){   //令A[i] += val
    while(x <= n){
        c[x] += val;
        x += lowbit(x);
    }
}

int find_(int x){           //二分查找
	int l=1,r=n,mid;
	while(l<r){
        mid=(l+r)>>1;
        int num=sum(mid);
        if(num<x)
			l=mid+1;
		else
			r=mid;
	}
	return l;
}

struct node1{
	ll h,v;
}a[MAXN];

ll b[MAXN];

bool cmp(node1 x,node1 y){
	 return x.h<y.h;
}

int main(){
    int t;
    t--;
    cin>>t;
    int kase=0;
    while(t--){
		kase++;
        memset(c,0,sizeof(c));
        memset(ans,0,sizeof(ans));
        scanf("%lld",&n);
        for(int i=1;i<=n;i++){
            add(i,1);
            scanf("%lld%lld",&a[i].h,&a[i].v);
        } 
        
        sort(a+1,a+n+1,cmp);
        int flag=1;
        for(int i=1;i<=n;i++){
            if(n-i-a[i].v<0) {flag=0;break;}
            int p=min(a[i].v,n-i-a[i].v)+1; //前面有多少个空位,包括本身,所以+1
            int pos=find_(p);
		
            add(pos,-1);
            ans[pos]=a[i].h;
        }
       
        printf("Case #%d:", kase);
		if (flag) {
			for (int i = 1; i <= n; i++) {
				printf(" %d", ans[i]);
			}printf("\n");
		}
		else printf(" impossible\n");
	}
    return 0;
}

猜你喜欢

转载自blog.csdn.net/sodacoco/article/details/86500229