poj-2828

13 篇文章 0 订阅
// 14460K   3766MS  G++
#include <stdio.h>
#include <string.h>

#define MAX 200100

struct TreeNode {
    int val;
    int l;
    int r;
    int insertPosNum;
};

typedef struct TreeNode TreeNode;

TreeNode tree[MAX*4];

int peopleNum;

struct PeopleJumpInfo {
    int pos;
    int val;
};

typedef struct PeopleJumpInfo PeopleJumpInfo;
PeopleJumpInfo jump[MAX];

int curFreeNodeId;

void buildTree(int l, int r, int pos) {
    tree[pos].l = l;
    tree[pos].r = r;
    tree[pos].val = -1;
    tree[pos].insertPosNum = r - l + 1;
    if (l == r) {
        return;
    } else {
        int mid = (l+r)>>1;
        buildTree(l, mid, pos<<1);
        buildTree(mid+1, r, pos<<1|1);
    }
}

void insert(int pos, int val, int nodeId) {
    // printf("insert %d %d\n", tree[nodeId].r, tree[nodeId].l);
    if (tree[nodeId].r == tree[nodeId].l) { // has narrow to 1 pos.
        tree[nodeId].val = val;
        tree[nodeId].insertPosNum = 0;
    } else {
        int leftInsertPosNum = tree[nodeId<<1].insertPosNum;
        int rightInsetyPosNum = tree[nodeId<<1|1].insertPosNum;
        tree[nodeId].insertPosNum--;
        if (leftInsertPosNum >= pos) {
            insert(pos, val, nodeId<<1);
        } else if (leftInsertPosNum < pos) {
            insert(pos - leftInsertPosNum, val, nodeId<<1|1);
        }
    }
}

int nodeNum;

void printFinalQueue(int nodeId) {
    if (tree[nodeId].l == tree[nodeId].r) {
        if (tree[nodeId].val >= 0) {
            nodeNum++;
            if (nodeNum < peopleNum) {
                printf("%d ", tree[nodeId].val);
            } else if (nodeNum == peopleNum){
                printf("%d\n", tree[nodeId].val);
            }
        }
        return;
    } else {
        printFinalQueue(nodeId<<1);
        printFinalQueue(nodeId<<1|1);
    }
}

int main() {
    while(scanf("%d", &peopleNum) != EOF) {
        memset(tree, 0, sizeof(tree));
        // for (int i = 0; i < MAX; i++) {
        //     tree[i].val = -1;
        // }
        nodeNum = 0;
        curFreeNodeId = 0;
        for (int i = 0; i < peopleNum; i++) {
            int pos, val;
            scanf("%d %d", &pos, &val);
            jump[i].pos = pos + 1;
            jump[i].val = val;
        }
        buildTree(1, peopleNum, 1); // root range: 1 <-> peoplenum
        // printf("buildTree\n");
        for (int i = peopleNum - 1; i >=0; i--) {
            insert(jump[i].pos, jump[i].val, 1);
        }
        // printf("printFinalQueue\n");
        printFinalQueue(1);
        // printf("\n");
    }
}

从纯线段树角度看,算是水题,但是从转化上看,感觉难度还是挺高的,唉,可能是自己悟性差,不过无所谓了,反正我也没跟别人比的资格。

最朴素的思路是模拟大法,但是绝壁TLE,因此直接pass, 后来想过用二叉树,这样二叉树的每个节点代表一个人,root是第一次进入队列的那个人,同时二叉树动态维护自己当前在队列的名次,这样以后有新的插队者,就不用像纯模拟那样的挪动整个数组了,只需插到合适的二叉树位置即可(左child是前一个位置,右child是后一个位置),最后做一个中序遍历就可以了,不过后来发现可能维护这个名次会比较费劲,对某个树左边插入一个新节点,该树的整个右子树都要更新,估计最后也是TLE的命。

最开始怎么都想不到用线段树,后来也是搜了下,才大概清楚,不过也用到了一些技法:逆序插入,同时线段树的每个节点,保存的是该节点对应的区间[l, r]还有多少个空缺可以填(一开始每个区间必然都有 r - l + 1个空位可填), 然后逆序的将人插入到线段树中,一直向下插,直到遇到 点(就是长度为1的区间,线段点),插入的操作是这样的:

比如有4个人:

原始数据:

4
0 77
1 51
1 33
2 69

首先简单转化一下,从插入的前一个位置变为插入的位置,+1即可:

1 77

2 51

2 33

3 69

那么逆序就是

3 69

3 33

3 51

1 77

然后逐个插入到线段树中,每个线段树节点保存了此区间还有多少个空位可以插入,

如果到了 点线段,那么就将此节点的val置为people的val,同时此 点区间的可插入空位数为0.

如果还是区间,那么先看一下左右子区各自的可插入空位: leftNum rightNum,

如果本次插入的节点的 pos(就是第一项)<= leftNum, 那么直接递归插入到 左子区间, 同时本区间的可插入位置数-1,

如果pos > leftNum, 那么就递归插入到右子区间,不过 pos 变为 pos - leftNum , 同样本区间插入位置数 -1.

最后对线段树做中序遍历,输出每个 点线段的 val即可。


一开始怎么都想不通,为何逆序插入加上标记区间可插入位置能保证正确性,

后来发现可以这么想:

可以把队列看一列槽, 一开始每个槽都是空的, 倒数第二个人p2在插入时的位置为k,那么在最后第一个人p1插入后, p2的最后实际位置一定是当前队列的

第k个空槽,可以这么认为的原因是,此时完全可以把p1从队列中去除(但是占据的槽不还原), 那么此时,队列的情况一定是 p2插入时的状况,

通过这种只考虑空槽  去除  已经入槽的人和其占据的位置 的影响,就可以求出某人在队列的实际位置了。

比较直白的想,将最终队列的后k个人都去掉,新队列中的第k-1人必然在自己插入时的位置,而前k-2的人可以看作是空槽.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值