POJ2828 Buy Tickets 线段树单点更新

题目链接:点击打开链接


题目大意:有n个人,第i行表示的是第i个人(其编号为val[ i ])插到第pos[ i ]个人的后面,问你n次插队之后的最终结果,按排队顺序输出每个人的编号。



分析:我们先把第一个人插入队中,然后顺序再插入一个人的时候,我们就需要更新其插入位置之后的所有位置,这样最坏的情况下就是O(n!)的复杂度了。但是如果换一种思维,我们注意到,最后一个人插入的位置即为他在队中的最终位置,其上一个人的插入位置pos实则就是队伍从头开始数的第pos个空位置,这样一来,我们每一次就能够直接把每个人直接插到其最终位置而不需要再去更新已经插入队中的人们的位置了,实现上可以用线段树来存储每个区间内的空位置数,对于当前插入的位置pos,我们需要判断当前节点左孩子的空位置数能否满足不小于pos+1,若满足则插入到左孩子中,都则插入右孩子中,对于叶子节点来说,存储插入到该节点的人的编号。复杂度也有O(n!)变为了O(logn)。



实现代码如下:

#include <cstdio>
#include <iostream>
using namespace std;
#define maxn 200005
#define lson rt<<1,l,m
#define rson rt<<1|1,m+1,r
int pos[maxn],val[maxn];
int emp[maxn<<2],ans[maxn<<2];
void build(int rt,int l,int r)
{
    emp[rt]=r-l+1;
    if(l==r) return ;
    int m=(l+r)>>1;
    build(lson);
    build(rson);
}
void update(int p,int v,int rt,int l,int r)
{
    if(l==r)
    {
        ans[rt]=v;
        emp[rt]--;
        return ;
    }
    int m=(l+r)>>1;
    if(p<=emp[rt<<1]) update(p,v,lson);
    else update(p-emp[rt<<1],v,rson);
    emp[rt]=emp[rt<<1]+emp[rt<<1|1];
}
void display(int rt,int l,int r)
{
    if(l==r)
    {
        printf("%d ",ans[rt]);
        return ;
    }
    int m=(l+r)>>1;
    display(lson);
    display(rson);
}
int main()
{
    int n;
    while(scanf("%d",&n)!=-1)
    {
        for(int i=1;i<=n;i++)
          scanf("%d%d",&pos[i],&val[i]);
        build(1,1,n);
        for(int i=n;i>0;i--)
          update(pos[i]+1,val[i],1,1,n);
        display(1,1,n);
        printf("\n");
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值