POJ P2828 Buy Tickets【线段树】【详细题解】

大概说一下题意:

n 个人,带着想要插队的位置(从 0 开始编号)和自身的一个 v a l 值进行插队,输出最后的 v a l 序列。

题意就是这样,正着想没什么思路那就想想逆推,毕竟正难则反。

其实这道题为什么要逆推也容易理解。序列始终是一个动态序列,我们无法在要求的时间内处理,而最后的序列是固定的,所以我们考虑逆推。

从后往前,那么每个人的位置其实都固定了。

比如样例:

0   20523
1   19243
1   3890
0   31492

为了方便处理,我们首先考虑将队伍的开头从 0 变为 1 ,那么输入就转为:

1   20523
2   19243
2   3890
1   31492

接下来我们模拟一遍逆推的过程:
整个队伍目前无人: 0     0     0     0
对于: 1   31492 ,需要的位置是 1 ,补上去: 31492     0     0     0
对于: 2   3890 ,需要的位置是 2 ,从空位置中补上去(一定要是空位置): 31492     0     3890     0
对于: 2   19243 ,需要的位置是 2 ,补上去: 31492     0     3890     19243
对于: 1   20523 ,补上去: 31492     20523     3890     19243
得到答案。

那么应该如何实现这个模拟呢。

当然就是线段树了。

每个节点除了维护区间端点之外再维护一个 N u m 值,表示该区间内还有 N u m 个空位置。

参考代码:

#include <cstdio>
const int Max=2e5+5;
struct Node{
    int X,Y,Num;
}Tree[Max<<3];
int N,Loc[Max],Val[Max],Ans[Max];
void MakeTree(int P,int X,int Y){
    Tree[P].X=X;Tree[P].Y=Y;
    if(X==Y){
        Tree[P].Num=1;
    } else if (X<Y){
        MakeTree(P<<1,X,X+Y>>1);
        MakeTree(P<<1|1,(X+Y>>1)+1,Y);
        Tree[P].Num=Tree[P<<1].Num+Tree[P<<1|1].Num;
    }
}
void Insert(int P,int X,int Y){
    if(Tree[P].X==Tree[P].Y){
        Tree[P].Num=0;Ans[Tree[P].X]=Y;return;
    }
    if(Tree[P<<1].Num>=X){
        Insert(P<<1,X,Y);
    } else {
        Insert(P<<1|1,X-Tree[P<<1].Num,Y);
    }
    Tree[P].Num=Tree[P<<1].Num+Tree[P<<1|1].Num;
}
int main(){
    int I,J,K;
    while(~scanf("%d",&N)){
        MakeTree(1,1,N);
        for(I=1;I<=N;I++){
            scanf("%d%d",&Loc[I],&Val[I]);Loc[I]++;
        }
        for(I=N;I>=1;I--){
            Insert(1,Loc[I],Val[I]);
        }
        for(I=1;I<=N;I++){
            printf("%d ",Ans[I]);
        }
        putchar('\n');
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/yanzhenhuai/article/details/81278268