Buy Tickets(POJ2828)线段树解法

先说说线段树版的POJ2828吧。
这道题让我调试了很长时间,最后才发现原来出现了一个很隐蔽的小问题上。。。。。。
这道题的题目意思很简单,就是一个队列,每个人都不停地插队,让你求出最后的队列是什么。

典型的逆向思维的题目,如果对线段树理解不深刻的话,很难想到用线段树实现,我们不是从前往后插入线段,而是倒序插入线段。
因为最后一个人一定在他想在的位置上,然后插入这个点,同时把这个点所在的线段记录的数-1来表示这个位置已经被占领过,因此下一个人想要进入这个位置的话,只能在后面插入。

 

program POJ2828;

const MaxN=200001;

type tre=record
		l,r,lc,rc,data:longint;
	end;

type atp=record
		num,val:longint;
	end;

var
	root,tot,n,i:longint;
	ans:array [0..MaxN] of longint;
	tree:array [0..MaxN*4] of tre;
	a:array [0..MaxN] of atp;

procedure build(var t:longint;l,r:longint);
begin
	inc(tot);
	t:=tot;
	tree[t].l:=l;
	tree[t].r:=r;
	tree[t].data:=r-l+1;//data域记录这个区间所含点的数目
	if l<r then 
		begin
			build(tree[t].lc,l,(l+r) div 2);	
			build(tree[t].rc,((l+r) div 2)+1,r);
		end;
end;

procedure qsort(l,r:longint);//用快排是为了让元素在线段树中的位置有序
var
	i,j,m:longint;
	t:atp;
begin
	i:=l;
	j:=r;
	m:=a[(l+r) shr 1].num;
	repeat
		while a[i].num<m do inc(i);
		while a[j].num>m do dec(j);
		if i<=j then
			begin
				t:=a[i];
				a[i]:=a[j];
				a[j]:=t;
				inc(i);
				dec(j);
			end;
	until i>j;
	if i<r then qsort(i,r);
	if l<j then qsort(l,j);
end;

procedure del(t,c,p:longint);
begin
	if tree[t].l=tree[t].r then//位置在线段树中的位置随之确定,同时将这段区间的元素置0,即这个点已被占领
		begin
			a[p].num:=tree[t].l;//该点在最后队列中的位置也确定
			tree[t].data:=0;
			exit;
		end;
	if c<=tree[tree[t].lc].data then del(tree[t].lc,c,p) else del(tree[t].rc,c-tree[tree[t].lc].data,p);//小于向左,大于向右,同时注意相减
	dec(tree[t].data);//递归减,将这个点之前覆盖过的区间的点的数都-1,即在区间中去掉了要插入的点
end;

begin
	assign(input,'POJ2828.in'); reset(input);
	assign(output,'POJ2828.out'); rewrite(output);

	while not eof do
		begin
			root:=0;
			tot:=0;
			readln(n);
			for i:=1 to n do readln(a[i].num,a[i].val);
			build(root,1,n);
			for i:=n downto 1 do del(root,a[i].num+1,i);//注意这里是a[i].num+1,因为建树的时候是从1~n建的,而题目中有0的存在
			qsort(1,n);
			for i:=1 to n do write(a[i].val,' ');
			writeln;
		end;

	close(input); close(output);
end.


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值