现在的位置: 首页 > 综合 > 正文

[poj 2828]Buy Tickets[线段树求第k大问题]

2018年03月17日 ⁄ 综合 ⁄ 共 1313字 ⁄ 字号 评论关闭

题意:

插队

posi    vali

编号为vali的人插在posi位置的人身后.

输出最终队伍.

/**
此题的关键在于逆序循环!
对于插队这项活动,总是越靠后越方便夺取胜利果实...
倒着循环,那么每次取的人的位置都能直接确定.
位于当前人的位置pos[i]+1,他的前面(截止到他来的时候)必然有pos[i]个人,而这pos[i]个人
一定是倒序循环尚且没有涉及的.因此要在当前人的前面留下pos[i]个位置(*).
已经被占了位置的是"未来"的事,所以对于pos[i]长的队来说并不存在.而倒着循环的时候
连续的队伍就变成了"找空位".
**/
#include <cstdio>
#include <cmath>
#include <algorithm>

using namespace std;

#define REP(i,n) for(int i=0;i<(n);++i)//全部循环
#define FOR(i,l,h) for(int i=(l);i<=(h);++i)//区间升序循环
#define FORD(i,h,l) for(int i=(h);i>=(l);--i)//区间降序循环

#define lson l,m,rt<<1  // 左儿子
#define rson m+1,r,rt<<1 | 1 // 右儿子
// rt即为root,表示当前子树的根

#define N 200010

int tree[N<<2];
int val[N],pos[N],ans[N];
int id;
void build(int l,int r,int rt)
{
    tree[rt]=r-l+1;//含端点的区间内空位个数,初始化为全空
    if(l==r)    return;
    int m=(l+r)>>1;
    build(lson);
    build(rson);
}
void update(int p,int l,int r,int rt)
{
    tree[rt]--;//插入了一个节点,那么一路找下来每个节点空位数都-1
    if(l==r)
    {
        id=l;//如果找到了叶子节点,则将插入的位置存在id中;返回
        return;
    }
    int m=(l+r)>>1;//选择中点
    if(tree[rt<<1]>=p) update(p,lson);//如果左儿子的空位数足以容纳
    //当前的人和他前面的所有人,则将当前人插入左儿子
    else
    {//否则,更新右儿子.此时插入的人相对于右儿子的位置变成p-=tree[rt<<1];
    //即是把左儿子的空余区间长度
        p-=tree[rt<<1];
        update(p,rson);
    }
}
int main(void)
{
    int n;
    while(scanf("%d",&n)==1)
    {
        build(1,n,1);//从1到n建树,n为队伍长度
        FOR(i,1,n)  scanf("%d%d",pos+i,val+i);//存在两个数组里
        FORD(i,n,1)//插在posi的后面,那么此人当前位置pos[i]+1
        {///逆!!序!!循!!环!!
            update(pos[i]+1,1,n,1);
            ans[id]=val[i];//节点插入的位置id就是最终的队列位置
        }
        FOR(i,1,n)  printf("%d%c",ans[i],i==n?'\n':' ');
    }
    return 0;
}

抱歉!评论已关闭.