POJ 2828 Buy Tickets(线段树)

题意:有n个人(每个人价值为val[i])依次从外面往队伍中插队,每次都会插在第pos[i]个位置,原本在pos[i]及其后面的人将会往后移一位,按最后队伍的顺序输出这些人价值。

思路:正着模拟显然超时,可以这样想,每次入队的人位置由两个因素影响,一来是他本身插队到的位置,第二个是后面的人插队使他往后移位。注意到最后差进来那个人位置一定不会改变,考虑到倒序插入队伍。现在我们观察插入到位置x的这个人,他前面应该已经插满了x个人,然后他后面还会有人插队。因此他应该预留x个空位给他前面的人,他自己占有第x+1个空位。插入这个人以后,更新每个点的空位数以便下一次插入时的查询,这些用线段树维护。

具体做法是:x[i]=1或0表示i点有一个空格或没有空格,那么如果我们用sum[i]代表前i个位置的空格总数,我们现在的任务就是找到一个最小的i,使得sum[i]=x+1。用线段树维护,二差查找。如果左子树上的空位数加上他的左边所有空位数之和大于等于x+1,则代表左子树上有点满足sum[i]=x+1,因此往左子树查找。否则往有子树查找。找到满足条件的点i后,使假想的x[i]=0,维护线段树。

#include<cstdio>
#define M ((L+R)>>1)
#define ls (o<<1)
#define rs (o<<1|1)
#define lson ls,L,M
#define rson rs,M+1,R
#define MAXN 200005
int val[MAXN],pos[MAXN],ans[MAXN],n,sumv[MAXN*3],s,v;
void pushup(int o) {sumv[o]=sumv[ls]+sumv[rs];}
void build(int o,int L,int R)
{
    if(L==R) {sumv[o]=1;return;}
    build(lson); build(rson);
    pushup(o);
}
void query(int o,int L,int R,int sum)//sum用来记录根o左端空位数和
{
    if(L==R){sumv[o]=0;ans[L]=v;return;}
    sum+sumv[ls]>=s? query(lson,sum):query(rson,sum+sumv[ls]);
//如果左子树上的空位数加上他的左边所有空位数之和大于等于x+1则代表左子树上有点满足sum[i]=x+1,因此往左子树查找。否则往有子树查找。
    pushup(o);
}
int main()
{
    while(~scanf("%d",&n))
    {
        for(int i=0;i<n;++i) scanf("%d%d",&pos[i],&val[i]);
        build(1,1,n);
        for(int i=n-1;i>=0;--i)
        {
            v=val[i],s=pos[i]+1;
            query(1,1,n,0);
        }
        printf("%d",ans[1]);
        for(int i=2;i<=n;++i) printf(" %d",ans[i]);
        puts("");
    }
    return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值