poj 2828 Buy Tickets 线段树

来源:互联网 发布:unity3d 渐变透明 编辑:程序博客网 时间:2024/05/16 14:51

这题也是用线段树来解,惭愧的说没在网上找过资料前真没想到这是用线段树做的

刚开始以为是直接把值往里添然后统计之前已经添过的人的数量,当然这个显然是错误的。

后来也是在看了别人的文章之后有了想法。

1、最后来插队的那个人直接能确定他的最终位置

2、从最后的人开始枚举,反过来去考虑整个插队的过程,如果一个人要插在第i个人的后面,那么也就说在他之前应当i个人,而当前已经插入到树中的人其实是在他之后才来的,所以,反过来一想,此时,必须在这个人前面留下i个位置,这i个位置是留给比他先来的人插的

按照这样的过程就能得到最后的队列了

代码中

node中的count表示在该节点下共有多少个空位置没被插队

update就是插队的过程(从最后那个人开始)

如果一个节点的左节点的count>i (i是他要插入的位置,如0的时候必须是留一个空位,这个空位就是把他自己插入的,1的时候留两个,以此类推),往左节点递归,知道最后l==r,否则,就得往右节点插入,而往右节点插入的时候必须算上左节点的空位数,所以得 i-左节点的空位数

(在代码里p就表示这里的i)

输出结果的话其实就是找到线段树的所有叶子,从左往右输出即可


#include <cstdio>#include <cstring>#define FF(i,n) for(int i=0; i<n; i++)const int MAX = 200010;struct node{    int count,val;}tree[MAX<<2];void push_up(int rt){    tree[rt].count = tree[rt<<1].count + tree[(rt<<1)+1].count;}void build(int l,int r,int rt){    if(l==r)    {        tree[rt].count = 1;        tree[rt].val = -1;        return;    }    int mid = (r+l)>>1;    build(l,mid,rt<<1);    build(mid+1,r,(rt<<1)+1);    push_up(rt);}void update(int p,int val,int l,int r,int rt){    if(l==r)    {        tree[rt].count = 0;        tree[rt].val = val;        return;    }    int mid = (r+l)>>1;    if(tree[rt<<1].count>p)    update(p,val,l,mid,rt<<1);    else update(p-tree[rt<<1].count,val,mid+1,r,(rt<<1)+1);    push_up(rt);}int cnt;void print(int l,int r,int rt){    if(l==r)    {        if(cnt) printf(" ");        printf("%d",tree[rt].val);        cnt++;        return;    }    int mid = (r+l)>>1;    print(l,mid,rt<<1);    print(mid+1,r,(rt<<1)+1);}int x[MAX],y[MAX];int main(){    int n;    while(scanf("%d",&n)==1)    {        build(1,n,1);        FF(i,n)        scanf("%d%d",&x[i],&y[i]);        FF(i,n)        update(x[n-1-i],y[n-1-i],1,n,1);        cnt=0;        print(1,n,1);        puts("");    }    return 0;}


原创粉丝点击