很经典的一道线段树问题,以前都没想到到线段树可以这样用。
/*Description:
春节将要到来了。小H希望回家过年。然后大家都知道火车是非常的难买,经常要排很长的队,但是小H为了买火车票拼了,不怕排队。
但是小H到了火车站的时候天已经黑了,他感觉非常的冷。他想为什么不找个问题思考来遗忘寒冷呢。然后他就想到了自己排队这个问题。
他想如果排队的每个人都有个固定的颜值,并且自己排队的时候经常有人插队,给出插队的人的位置和颜值,那么最后的颜值序列是多少呢?
Input:
有多组测试数据。
每组测试数据第一行为N (1<=N<=200000),表示有多少个人插队。
接下有的N行有一对数,第一个数表示插队的人要插得位置pos(0<=pos),第二个数表示他的颜值val(0<=val<=32767)。
Output:
输出最终的颜值序列。
Sample Input:
4
0 77
1 51
1 33
2 69
4
0 20523
1 19243
1 3890
0 31492
Sample Output:
77 33 69 51
31492 20523 3890 19243
友情提示:
就第一组数据来说,
第一个颜值为77的插入第0个位置之后序列是:77
第二个颜值为51的插入第1个位置之后序列是:77 51
第三个颜值为33的插入第1个位置之后序列是:77 33 51
第四个颜值为69的插入第2个位置之后序列是:77 33 69 51
线段树:倒序插入,这样POS的意义就变为找到这么一个位置可以放置这个人, 使得从这个位置往前数共有POS个空位。
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_N (1<<19)
int dat[MAX_N*2];//线段树节点
int cnt[MAX_N];
int pos[MAX_N];//位置 也表示前面需要多少空位
int seq[MAX_N];//记录结果
//以0为根节点
int e(int x)//返回log2(x-1)
{
--x;
int i=0;
while(x){x/=2;++i;}
return i;
}
void init_dat(int root,int l,int r)
{//初始化线段树的值,初始值为下辖区间
dat[root]=r-l+1;
if(l==r)
return;
init_dat(root*2+1,l,(l+r)/2);
init_dat(root*2+2,(l+r)/2+1,r);
}
void update(int r)
{//向上更新
while(r)
{
--dat[r];
r=(r-1)/2;
}
--dat[r];
}
void insert(int p,int c,int l)
{//为c颜值的寻找位置
int root=0;
int limit=l*2;
while(root*2<limit)
{//假如左边区间的空位>p 表示需要找的节点在左区间
//否则,说明在右边,注意往右需要减去左区间的空位数
if(dat[root*2+1]>p)
root=root*2+1;
else {
p-=dat[root*2+1];
root=root*2+2;
}
}
seq[root]=c;
update(root);
}
void solve(int n)
{
int l=(1<<e(n))-1;//最底层的最左边的叶子节点的编号
init_dat(0,0,l);
for(int i=n-1;i>=0;--i)
{//从后向前逐个寻位
insert(pos[i],cnt[i],l);
}
printf("%d",seq[l]);
for(int i=1;i<n;++i)
printf(" %d",seq[l+i]);
printf("\n");
}
int main()
{
int n;
while(~scanf("%d",&n))
{
for(int i=0;i<n;++i)
scanf("%d%d",pos+i,cnt+i);
solve(n);
}
return 0;
}