题意
有N个人排名买票,现在给出每个人要插入的位置pos(0<=pos<=N-1)以及他的价值val。在插入N个人后,会构成一个新序列。现在让你按顺序 输出新序列中每个位置pos对应的价值val
思路
最后一次插入某个位置的人,他的位置不会再改变,所以我们应该从后往前插入。用sum记录所在空间剩余的位置。对于插入在pos位置的人他前边一定要有pos-1(1~n 建树)个剩余位置。(0~n 建树则为 pos个剩余位置)
访问结点时,如果左子树的剩余位置大于或等于 pos 就访问左子树(sum[rt<<1] >= pos),否则就访问右子树。访问右子树时需要用pos减去左子树的剩余位置( pos-sum[rt<<1] ),即整个线段树的第pos个空位(空叶子结点),也是在右儿子那的第pos-sum[rt<<1]个空位。
#include<cstdio>
const int maxn=200000;
int sum[maxn<<2],pos[maxn<<2],val[maxn<<2],ans[maxn<<2]; //ans存储插入的值
void pushup(int rt)
{
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void bulid(int l,int r,int rt)
{
if(l==r)
{
sum[rt]=1; //初始化每个叶子结点剩余位置为 1
return ;
}
int m=(l+r)>>1;
bulid(l,m,rt<<1);
bulid(m+1,r,rt<<1|1);
pushup(rt);
}
void update(int p,int v,int l,int r,int rt)
{
if(l==r)
{
ans[rt]=v;
sum[rt]--;
return ;
}
int m=(l+r)>>1;
if(sum[rt<<1]>=p) //判断插入左子树还是右子树
update(p,v,l,m,rt<<1);
else
update(p-sum[rt<<1],v,m+1,r,rt<<1|1);
pushup(rt);
}
void print(int l,int r,int rt)
{
if(l==r)
{
printf("%d ",ans[rt]);
return ;
}
int m=(l+r)>>1;
print(l,m,rt<<1);
print(m+1,r,rt<<1|1);
}
int main()
{
int n;
while(~scanf("%d",&n))
{
bulid(1,n,1);
for(int i=0;i<n;i++)
scanf("%d%d",&pos[i],&val[i]);
for(int i=n-1;i>=0;i--)
update(pos[i]+1,val[i],1,n,1);
print(1,n,1);
printf("\n");
}
return 0;
}
参考博客
http://www.cnblogs.com/ACMan/archive/2012/07/11/2586793.html