题目链接
题目大意
火车站有n(n ≤ 200000)个人要排队,它们是按顺序到达,到达后要插队,现在告诉你每个人每次插队排在当前队伍第几个人的后面以及它们的权值,输出最终队伍的权值序列。
分析
按照题意,每个人插队时的位置都是相对那个时候的队伍状态而言的,因此是动态变化,但是经过分析我们可以发现,最后一个人的位置是一定确定的,因此我们可以倒序考虑每个人的插队,依次确定每个人的位置。举个栗子:比如现在我考虑第n-1个人的插队,它要插到他到达时队伍第x个人的后面,而此时第n个人的位置已经确定,相当于那个位置已经被占领,我们只需在剩余的位置中找到第x+1个给第n-1个人即可,以此类推。
所以我们可以用线段树维护区间中剩余位置的数量sum[],插队时相当于单点更新,点的位置相当于查找第K大元素:当左子区间中剩余元素大于等于K时,就在左子区间中查找第K个位置,否则就在右子区间中查找第K-sum[rs]个位置,找到位置后更新权值。最后遍历线段树,依次输出叶子节点的权值即可。
代码
#include <iostream>
#include <cstdio>
#define ls (rt<<1)
#define rs (rt<<1|1)
using namespace std;
const int MAXN=200010;
struct Node
{
int p,v;
}node[MAXN];//记录每个人的插队信息
int n,sum[MAXN<<2],val[MAXN<<2],cnt;
//sum[]用线段树维护区间中剩余位置数,val[]为结点权值
void PushUp(int rt)
{
sum[rt]=sum[ls]+sum[rs];
}
void Build(int l,int r,int rt)
{
if (l==r)
{
sum[rt]=1;//初始都有空位
return;
}
int mid=(l+r)>>1;
Build(l,mid,ls);
Build(mid+1,r,rs);
PushUp(rt);
}
void Update(int p,int v,int l,int r,int rt)//在区间[l,r]中找第p个剩余位,把它的权值更新为v
{
if (l==r)
{
if (sum[rt]==1)
{
sum[rt]=0;
val[rt]=v; //占位并更新权值
}
return;
}
int mid=(l+r)>>1;
if (p<=sum[ls]) //查找第K大元素的思想
Update(p,v,l,mid,ls);
else
Update(p-sum[ls],v,mid+1,r,rs);
PushUp(rt);
}
void Print(int l,int r,int rt)//遍历线段树结点,输出叶子结点权值
{
if (l==r)
{
cnt++;
printf("%d",val[rt]);
if (cnt<n)
printf(" ");
else
printf("\n");
return;
}
int mid=(l+r)>>1;
Print(l,mid,ls);
Print(mid+1,r,rs);
}
int main()
{
int i;
while (scanf("%d",&n)!=EOF)
{
for (i=1;i<=n;i++)
{
scanf("%d%d",&node[i].p,&node[i].v);
node[i].p++;//排在第p个人后面就是要占第p+1个位置
}
Build(1,n,1);
for (i=n;i>=1;i--) //倒序插队
{
int p=node[i].p;
int v=node[i].v;
Update(p,v,1,n,1);
}
cnt=0;
Print(1,n,1);
}
return 0;
}
注:当输入输出较多时,避免使用输入输出流,容易TLE.