POJ - 2828 Buy Tickets(线段树单点更新)

题意:

有n个的排队,每一个人都有一个val来对应,每一个后来人都会插入当前队伍的某一个位置pos。要求把队伍最后的状态输出。

解析:

逆向思维。我们可以这样考虑,最后一个人一定会得到当前队伍他想要的位置,如果我们往前一个阶段,倒数第二个人也一定能得到他想要的位置…
也就是说,我们可以这样处理,我们把最后一个人插入,然后忽略它,再把倒数第二个人插入。即,我们找出当前队伍他想要插入的位置pos的真正坐标就可以。
然后去更新整个队伍的长度。如此循环,直到最后一个人。
具体实现:
sumv[]记录该区间,目前还剩多少个空位。每一次modefy(),即插入的时候,如果该节点左儿子sumv >=pos,那么只要在左儿子找就可以了。
否则 要在右儿子中找 ,此时 pos = (pos-左儿子sumv)。

AC代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2e5 + 10;
int n;
int sumv[N << 2], ans[N];
void build(int o, int L, int R) {
    if(L == R) {
        sumv[o] = 1;
        return ;
    }
    int M = (L + R)/2;
    build(o*2, L, M);
    build(o*2+1, M+1, R);
    sumv[o] = sumv[o*2] + sumv[o*2+1];
}

int p[N], v[N];
void modify(int o, int L, int R, int id) {
    if(L == R) {
        sumv[o]--;
        ans[L] = v[id];
        return ;
    }
    int M = (L + R)/2;
    if(sumv[o*2] >= p[id]) {
        modify(o*2, L, M, id);
    }else {
        p[id] -= sumv[o*2];
        modify(o*2+1, M+1, R, id);
    }
    sumv[o] = sumv[o*2] + sumv[o*2+1];
}

int main() {
    while(~scanf("%d", &n)) {
        build(1, 1, n);

        for(int i = 1; i <= n; i++) {
            scanf("%d%d", &p[i], &v[i]);
            p[i]++;
        }
        for(int i = n; i >= 1; i--) {
            modify(1, 1, n, i);
        }

        printf("%d", ans[1]);
        for(int i = 2; i <= n; i++) {
            printf(" %d", ans[i]);
        }puts("");
    }
    return 0;
}

你可能感兴趣的:(poj,2828)