poj 2828 线段树

说实话,作为线段树的第一题,当时确实不知道怎样把题目联系到线段树上。当时首先想到的是用链表做一个队列,自然地就TLE了

看别人代码写的,思路有些麻烦(对于我这菜鸟来说==):


1.线段树中的每个节点 记录这个区间中的空位置数量。而在处理输入时,需要从后向前处理(因为后面的 人可能会把前面的人的位置抢了),如题目样例:

(X代表空位置)

i         XX(69)XXX

II        X(33)(69)XXX

III       X(33)(69)(51)XX

IV       (77)(33)(69)(51)XX


2.是用线段树 记录空位置的个数,每次插入的时候将这个人放在第pos[i] 个空格的地方(注意,由于后面人可能把这个位置已经占了,所以很有可能不会正好放在全局的第pos[i]空格上),所以就用到了线段树的区间查询功能:

(1) 从线段树睐根出发,向下递归空位,若左子树空位数 >= pos[j],则要需找的空位在左子树上,否则要寻找的空位在右子树的第 (j - 左子树空位数) 空位置上,直到找到叶节点,可知此人排列位置为lp(叶节点lp==rp)

 (2)维护线段树,本题目中只牵扯到空位置减少,所以维护只需要从叶节点向上 使该结点所代表区间的空位减一即可

//方法: 从最后一个入队的人开始向前考虑,假设人pi,用线段树保存 队列中空格位置信息 
//从线段树中 以logn 时间复杂度 计算出pi应该 放入的空格,同时要更新线段树的信息,此时point[]
//数组就 返回空格在 线段树中的下标,从而进行线段树的更新
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <string>
#include <string.h>
#include <memory.h>

using namespace std;

const int maxn = 200000 + 20;

int pos[maxn];
int val[maxn];	//POS val 分别存储题目中要求的值
int trsize[maxn * 2];	//存储线段树数组 trsize[i] 表示相应区间下 空位置个数
int ans[maxn];	//最后输出 的序号 如ans[i] 表示队列中第i个人的序号
int peo_num;	//总人数
int point[maxn]; //point[t] 表示最后排列中 第t个位置在 trsize中的下标

void build_tree(int lp,int rp,int i)		//从结点i出发,构造区间[lp,rp]的区间树
{
	trsize[i] = rp - lp + 1;			//该区间下有多少个空格
	if (lp == rp)
	{
		point[lp] = i;					//记录空格在 线段树中的位置
		return;				
	}

	int mid = (lp + rp) / 2;
	build_tree(lp, mid, 2 * i);
	build_tree(mid + 1, rp, 2 * i + 1);
}


int require(int sum, int lp, int rp, int i)		//计算第sum个空格在线段树中的位置,即返回值为 (1--n)
{
	if (lp == rp)
		return lp;
	int mid = (lp + rp) / 2;

	if (trsize[2 * i] >= sum)			//如果左区间的空格数量足够,则空格应该在左区间
		return require(sum, lp, mid, i * 2);
	return require(sum - trsize[2 * i], mid + 1, rp, i * 2 + 1);
}

void keep(int i)			//维护线段树
{
	while (i > 0)
	{
		trsize[i]--;
		i /= 2;
	}
}

void init()
{
	for (int i = 1; i <= peo_num; i++)
		scanf("%d %d", &pos[i], &val[i]);
}

void solve()
{
	memset(trsize, 0, sizeof(trsize));
	memset(ans, 0, sizeof(ans));
	memset(point, 0, sizeof(point));

	build_tree(1, peo_num, 1);
	
	for (int i = peo_num; i >= 1; i--)
	{
		int t = require(pos[i] + 1, 1, peo_num, 1);
		ans[t] = i;	//第t个位置上 那个人的序号
		//维护线段树
		keep(point[t]);
	}

	for (int i = 1; i <= peo_num - 1; i++)
		printf("%d ", val[ans[i] ]);
	printf("%d\n", val[ans[peo_num]]);
}

int main()
{
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
	while (1 == scanf("%d", &peo_num))
	{
		init();
		solve();
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值