poj2828 Buy Tickets

先将位置信息存入数组p,然后倒序插入线段树中,然后根据线段树区间空位的信息将v插入ans数组中合适的位置。利用了倒序插入时第i个元素最终在ans中的位置为插入时从ans左往右第i+1个空位处的这个规律。线段树的作用不是存储最后的v,而是为v插入ans中的合适位置提供标准。将查找合适位置的复杂度降到O(logn)


在插入线段树时的标准是,区间左右孩子节点所剩的空位数。


#include <cstdio>
#include <string>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <ctype.h>
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1

using namespace std;

const int maxn = 200010;
int tree[maxn << 2];
int p[maxn], v[maxn], ans[maxn];

void PushUP(int rt) {
    tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];
}

void build(int l, int r, int rt) {
    if (l == r) {
        tree[rt] = 1;    ///开始的时候初始化每个节点存储量为1
        return ;
    }
    int m = (l + r) >> 1;
    build(lson);
    build(rson);
    PushUP(rt);
}

void update(int p, int v, int l, int r, int rt) {
    if (l == r) {
        ans[l] = v;     ///根据线段树找到了v在ans中的合适位置,插入
        tree[rt] = 0;   ///此时v占了这个点的位置,将这个叶节点的容量置0
        return ;
    }
    int m = (l + r) >> 1;
    if (p <= tree[rt << 1]) update(p, v, lson);   ///此时若左子树的空位足够多,则递归插入左子树
    else update(p - tree[rt << 1], v, rson);    ///否则,算上左子树的空位,将剩下的空位利用右子树进行递归插入
    PushUP(rt);
}

int main()
{
    int N;
    while(~scanf("%d", &N)) {
        build(1, N, 1);
        for (int i = 1; i <= N; i++) {
            scanf("%d%d", &p[i], &v[i]);
        }
        for (int i = N; i >= 1; i--) {
            update(p[i] + 1, v[i], 1, N, 1);   ///此处倒序进行插入,原因是便于确定最后节点的位置
        }
        for (int i = 1; i <= N - 1; i++) {
            printf("%d ", ans[i]);
        }
        printf("%d\n", ans[N]);
    }
    return 0;
}


你可能感兴趣的:(算法,线段树,ACM,poj,hduoj)