poj 2828 Buy Tickets 線段樹

這題也是用線段樹來解,慚愧的說沒在網上找過資料前真沒想到這是用線段樹做的

剛開始以爲是直接把值往裏添然後統計之前已經添過的人的數量,當然這個顯然是錯誤的。

後來也是在看了別人的文章之後有了想法。

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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章