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;
}


你可能感兴趣的:(POJ2828 Buy Tickets 线段树单点更新)