线段树
题意:先给定一个n,然后依次给出第i个人前面的人数和他价值,让其插队,求最终序列。
初学者表示虽然是从线段树专题上看到的这个题,仍然想不到如何建树。。。逆序建树,因为越往后的人位置越确定,最后一个人的前面有多少人是肯定确定的,最后一个人安排好后倒数第二个人的也就确定了,依次类推,直至所有人的位置都确定。首先建树,叶子节点值设为1(代表此处有一个位置),其他节点值为左孩子和右孩子数值之和。然后依次从后插入,插入的同时维护线段树,直至插入完成,即是最终序列,输出即可。
之所以初始化为1,是因为在最后完成排序后所有人的位置都是确定的,那么,从后向前,最后一个人的位置是确定的,他的位置确定之后,就能确定他来之前队列的状态,在该状态的基础下能确定倒数第二个人的位置,依次便能确定所有人的位置。第一次做这个题目没想这么细,惭愧。
#include <iostream>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
int sum[811111];
int val[811111];
int a[200001];
int b[200001];
int p;
void pushup(int rt)
{
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build(int l,int r,int rt)
{
sum[rt]=1;
val[rt]=1;
if (l==r)
return ;
int m=(l+r)>>1;
build(lson);
build(rson);
pushup(rt);
}
void pushdown(int v,int l,int r,int rt)
{
if (l==r)
{
sum[rt]=0;
val[rt]=v;
return ;
}
int m=(l+r)>>1;
if (sum[rt<<1|1]==0)
pushdown(v,lson);
else
pushdown(v,rson);
pushup(rt);
}
void update(int c,int v,int l,int r,int rt)
{
if (sum[rt]==c+1)
{
pushdown(v,l,r,rt);
return;
}
int m=(l+r)>>1;
if (sum[rt<<1]>=c+1)
update(c,v,lson);
else
update(c-sum[rt<<1],v,rson);
pushup(rt);
}
void query(int l,int r,int rt)
{
if (l==r)
{
if (p)
printf(" %d",val[rt]);
else
{
printf("%d",val[rt]);
p=!p;
}
return ;
}
int m=(l+r)>>1;
query(lson);
query(rson);
}
int main()
{
int n;
while (cin>>n)
{
build(1,n,1);
p=0;
for (int i=0;i<n;i++)
{
scanf("%d%d",&a[i],&b[i]);
}
for (int i=n-1;i>=0;i--)
{
update(a[i],b[i],1,n,1);
}
query(1,n,1);
cout<<endl;
}
}