HDU - 5493 Queue(二分+树状数组)

题目链接:传送门

题意:给你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;
}

猜你喜欢

转载自blog.csdn.net/liexss/article/details/82432775