POJ 2828: Buy Tickets(线段树)

扯淡:

这题要赞。

因为卡了我两次……

第三次终于做出来了。。。好吧是看了别人代码之后T T。太弱太弱。。。

题意:

有n个人排队,但他们会插队。他们按先后顺序来到,每次每个人选择插在Pos[i]这个位置,问最后的排队情况(每个人用其val值表示)。(1 ≤ N ≤ 200,000)

思路:

容易想到,最后一个人,插哪,那它就在哪。

也就是说,一个人的最后位置,只会被它后面的人影响到。

再分析,就会发现,如果一个人插在Pos[i]这个位置,那么表示,它插入的时候,前面有Pos[i]个人(废话)。那么我们倒着计算的时候,由于他后面的人已经进来了,那么第i个人站的实际位置,是在它前面还有pos[i]个空位的位置(加上它本身这个空位一共是pos[i]+1个)

这里,粗暴的是想用二分+线段树查找的算法,这样是logNlogN,实际上可以直接在线段树上完成。这点很赞!!!!!

 

代码:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#define N 200010
int tree[N<<2];
int pos[N], a[N], val[N];

void build() {
    memset(tree,0,sizeof(tree));
}

//int query(int L, int R, int l, int r, int rt) {
//    if (L <= l && r <= R) {
//        return tree[rt];
//    }
//    int mid = (l+r)/2;
//    int ans = 0;
//    if (L <= mid) ans += query(L, R, l, mid, rt<<1);
//    if (R > mid) ans += query(L, R, mid+1, r, rt<<1|1);
//    return ans;
//}

void insert(int x, int v, int l, int r, int rt) {
    //printf("[%d,%d] rt = %d\n", l, r, rt);
    if (l == r) {
        tree[rt]++;
        a[l] = v;
        return ;
    }
    int mid = (l+r)/2;
    if (x <= (mid-l+1-tree[rt<<1])) insert(x,v,l,mid,rt<<1);
    else insert(x-(mid-l+1-tree[rt<<1]),v,mid+1,r,rt<<1|1);

    tree[rt] = tree[rt<<1] + tree[rt<<1|1];
}

int main() {
    int n;
    while (scanf("%d", &n) != EOF) {
        for (int i = 0; i < n; i++) {
            scanf("%d%d", &pos[i], &val[i]);
        }
        build();

        for (int i = n-1; i >= 0; i--) {
            insert(pos[i]+1,val[i],0,n,1);
        }
        
        for (int i = 0; i < n; i++) {
            printf(i==0?"%d":" %d", a[i]);
        }puts("");
    }
    return 0;
}

 

posted on 2014-04-09 14:38  ShineCheng  阅读(145)  评论(0编辑  收藏  举报

导航