【ybtoj高效进阶 21265】排队问题(fhq-Treap)(构造)

排队问题

题目链接:ybtoj高效进阶 21265

题目大意

给你每个人的身高和要求,每个人的身高都不同。
每个人的要求是要它左边比他高的人或右边比他高的人个数是它给出的值。
然后要你找出字典序最小的满足的身高序列。

思路

我们考虑把人按身高从小到大排序,然后我们不难想到如果把身高一样的排在一起,然后我们一起填,那这个时候,我们其实就是要它填入的位置左边或右边是它给出的值即可。

首先判不可能的条件,即比它大的个数小于它的值。
那接着我们会有两个可以放的位置,那由于要字典序最小,我们是从小到大放的,所以我们就调哪个靠左放哪个即可。
那放我们考虑有点类似链表的插入,这里用的是 fhq-Treap。

然后由于你要消除相同身高的影响,我们考虑先放位置后的,再放位置前的。
所以你排个序就好了。

代码

#include<cstdio>
#include<cstdlib>
#include<algorithm>

using namespace std;

struct node {
	int a, b;
}a[100001];
int n;

bool cmp(node x, node y) {
	if (x.a != y.a) return x.a > y.a;
	return x.b > y.b;
}

struct fhq_treap {//平衡树
	int rd[100001], ls[100001], rs[100001];
	int a[100001], sz[100001], tot, root;
	
	int make_new(int val) {
		int now = ++tot;
		a[now] = val; sz[now] = 1;
		ls[now] = rs[now] = 0;
		rd[now] = rand();
		return now;
	}
	
	void up(int now) {
		sz[now] = sz[ls[now]] + sz[rs[now]] + 1;
	}
	
	int merge(int x, int y) {
		if (!x || !y) return x + y;
		
		if (rd[x] > rd[y]) {
			rs[x] = merge(rs[x], y);
			up(x);
			return x;
		}
		else {
			ls[y] = merge(x, ls[y]);
			up(y);
			return y;
		}
	}
	
	pair <int, int> split_rnk(int now, int rnk) {
		if (!now) return make_pair(0, 0);
		if (!rnk) return make_pair(0, now);
		
		pair <int, int> re;
		if (rnk <= sz[ls[now]]) {
			re = split_rnk(ls[now], rnk);
			ls[now] = re.second;
			up(now);
			re.second = now;
		}
		else {
			re = split_rnk(rs[now], rnk - sz[ls[now]] - 1);
			rs[now] = re.first;
			up(now);
			re.first = now;
		}
		return re;
	}
	
	int insert(int val, int rnk) {
		int now = make_new(val);
		pair <int, int> re = split_rnk(root, rnk);
		return merge(merge(re.first, now), re.second);
	}
	
	void write(int now) {
		if (!now) return ;
		
		write(ls[now]);
		printf("%d ", a[now]);
		write(rs[now]);
	}
}T;

int main() {
//	freopen("queue.in", "r", stdin);
//	freopen("queue.out", "w", stdout);
	
	srand(1919810);
	
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
		scanf("%d %d", &a[i].a, &a[i].b);
	
	sort(a + 1, a + n + 1, cmp);
	
	for (int i = 1; i <= n; i++) {
		int ii = i;
		if (a[ii].b > i - 1) {//比它高的人没有那么多
			printf("impossible");
			return 0;
		}
		if ((i - 1) - a[ii].b < a[ii].b) a[ii].b = (i - 1) - a[ii].b;//反过来更前面
		while (ii < n && a[ii + 1].a == a[ii].a) {//把相同的都找出来
			ii++;
			if (a[ii].b > i - 1) {//跟前面一个道理
				printf("impossible");
				return 0;
			}
			if ((i - 1) - a[ii].b < a[ii].b) a[ii].b = (i - 1) - a[ii].b;
		}
		sort(a + i, a + ii + 1, cmp);//重新排一次序,排出 b(因为你前面可能会改 b)
		for (int j = i; j <= ii; j++) {//一个一个放
			T.root = T.insert(a[j].a, a[j].b);
		}
		i = ii;
	}
	
	T.write(T.root);
	
	return 0;
}
上一篇:android 7.1悬浮窗系统权限问题


下一篇:Powershell学习笔记——Powershell控制台和脚本文件