POJ 2828 Buy Tickets 线段树

题意:买火车票时总有人插队,每人有两个属性,插入位置pos[i] (pos[i]代表此人前面的总人数),以及人物编号 value[i]。在每一轮输入后,按队伍顺序输出人物的编号。
题解:处理点的线段树。从后往前推,则可以避免后来人对pos的影响,这样一来每个人的pos都是精确的。 

#include <iostream>
using namespace std;

#define MAX 200005
int pos[MAX], value[MAX], out[MAX];

struct item
{
	int left, right, person;/*person代表当前节点区间可容纳的人数*/
} node[MAX*3];

void build_tree ( int l, int r, int root )
{
	node[root].left = l;
	node[root].right = r;
	node[root].person = r - l + 1;
	if ( l == r ) return;
	int mid = ( l + r ) / 2;
	build_tree ( l, mid, root * 2 );
	build_tree ( mid + 1, r, root * 2 + 1 );
}

int update ( int p, int root )
{
	node[root].person --; /*每插入一个人,则节点区间的容量减小1*/
	if ( node[root].left == node[root].right ) /*当左右坐标相等时,已经确定到点,即插入者得位置确定*/
		return node[root].left;
	int lchild = root * 2;
	int rchild = root * 2 + 1;
	if ( node[lchild].person >= p ) /*r若左子树的容量大于插入者的位置,那么插入者的最终位置一定在左子树的区间内*/
		return update( p, lchild );
	else
	{
		p -= node[lchild].person; /* 假设某人的需要给前面的人留5个空位,左子树可以提供3个,那么就还需右子树提供2个*/
		return update ( p, rchild );
	}
}

int main()
{
	int n, index, i;
	while ( scanf("%d",&n) != EOF )
	{
		build_tree( 1, n, 1 );
		for ( i = 1; i <= n; ++i )
			scanf("%d%d", &pos[i], &value[i]);
		for ( i = n; i >= 1; --i )
		{
			index = update ( pos[i]+1, 1 );
			out[index] = value[i];
		}
		for ( i = 1; i <= n; ++i )
			printf("%d ",out[i]);
		putchar('\n');
	}
	return 0;
}


你可能感兴趣的:(POJ 2828 Buy Tickets 线段树)