HUT-4302 Holedox Eating 线段树

该题说的是一个小东西生活在管道内,长度为L,里面有一些蛋糕,现在动态的给定蛋糕的坐标,小东西的坐标初始话为0,当指令为吃蛋糕的时候,小东西将会去吃最临近的蛋糕。现在问你小东西在Q次询问下要走多远的距离。

这题用树状数组+二分查找超时了。。 构造出一棵线段树,每个节点保留区间中所有的蛋糕数以及左右蛋糕最近的坐标。维护好线段树即可。

代码如下:

#include <cstdlib>
#include <cstdio>
#include <cstring>
using namespace std;

int L, Q;

struct Node
{
    int num, l, r, left, right;
}e[400005];

void build(int p, int l, int r)
{
    e[p].l = l, e[p].r = r;
    e[p].left = e[p].right = -1;  // 由于0为有效位,所以以-1特殊标记
    e[p].num = 0;
    if (l != r) {
        int mid = (l + r) >> 1;
        build(p<<1, l, mid);
        build(p<<1|1, mid+1, r);
    }
}

void update(int p)
{
    e[p].num = e[p<<1].num + e[p<<1|1].num;
    if (e[p<<1].left != -1) {
        e[p].left = e[p<<1].left;
    }
    else {
        e[p].left = e[p<<1|1].left;
    }
    if (e[p<<1|1].right != -1) {
        e[p].right = e[p<<1|1].right;
    }
    else {
        e[p].right = e[p<<1].right;
    } 
}

void modify(int p, int x, int val)
{
    if (e[p].l == e[p].r) {  // 一定就是我们要找的点
        e[p].num += val;
        if (e[p].num > 0) {
            e[p].left = e[p].right = e[p].l;
        }
        else {
            e[p].left = e[p].right = -1;
        }
        return;
    }
    else {
        int mid = (e[p].l + e[p].r) >> 1;
        if (x <= mid) {
            modify(p<<1, x, val);
        }
        else {
            modify(p<<1|1, x, val);
        }
    }
    update(p);
}

int rquery(int p, int l, int r)
{
    if (l == e[p].l && e[p].r == r) {
        return e[p].right;
    }
    int mid = (e[p].l + e[p].r) >> 1;
    if (r <= mid) {
        if (e[p<<1].num != 0) {
            return rquery(p<<1, l, r);
        }
        else return -1;
    }
    else if (l > mid) {
        if (e[p<<1|1].num != 0) {
            return rquery(p<<1|1, l, r);
        }
        else return -1;
    }
    else {
        int ans = rquery(p<<1|1, mid+1, r);
        if (ans == -1) {
            ans = rquery(p<<1, l, mid);
        }
        return ans;
    }
}

int lquery(int p, int l, int r)
{
    if (l == e[p].l && r == e[p].r) {
        return e[p].left;
    }
    int mid = (e[p].l + e[p].r) >> 1;
    if (r <= mid) {
        if (e[p<<1].num != 0) {
            return lquery(p<<1, l, r);
        }
        else return -1;
    }    
    else if (l > mid) {
        if (e[p<<1|1].num != 0) {
            return lquery(p<<1|1, l, r);
        }
        else return -1;
    }
    else {
        int ans = lquery(p<<1, l, mid);
        if (ans == -1) {
            ans = lquery(p<<1|1, mid+1, r);
        }
        return ans;
    }
}

int goleft(int &pos, int &dir, int left)
{
    int ans;
    dir = 0;
    ans = pos - left;
    pos = left;
    modify(1, left, -1);
    return ans;
}

int goright(int &pos, int &dir, int right)
{
    int ans;
    dir = 1;  // 向右走,更新方向和地址
    ans = right - pos;
    pos = right;
    modify(1, right, -1);
    return ans;
}

int solve(int p, int &pos, int &dir)
{
    int left = rquery(1, 0, pos);
    int right = lquery(1, pos, L);
    if (left == -1 && right == -1) {
        return 0;
    }
    if (left == -1 || right == -1) {
        // 有某一部分没有豆子,第一次一定是在这个区间内的
        if (right != -1) {
             return goright(pos, dir, right);
        }
        else if (left != -1) {
             return goleft(pos, dir, left);
        }
    }
    else {
        if (left == right) {
            modify(1, left, -1);
            return 0;
        }
        else {
            if (pos - left != right - pos) {
                if (pos - left < right - pos) {
                    return goleft(pos, dir, left);
                }
                else {
                    return goright(pos, dir, right);
                }
            }
            else {
                if (dir == 0) { // 玩左走
                    return goleft(pos, dir, left);
                }
                else {
                    return goright(pos, dir, right);
                }
            }
        }
    }
}

int main()
{
    int T, ca = 0, op, x, dir, pos, result;
    scanf("%d", &T);
    while (T--) {
        dir = pos = result = 0;  // 初始的方向并没有影响,0为左,1为右
        scanf("%d %d", &L, &Q);
        build(1, 0, L);
        while (Q--) {
            scanf("%d", &op);
            if (op == 0) {
                scanf("%d", &x);
                modify(1, x, 1);  // 对于该题而言只存在单点更新
            }
            else {
                result += solve(1, pos, dir);
            }
        }
        printf("Case %d: %d\n", ++ca, result);
    }
    return 0;
}
posted @ 2012-07-20 21:38  沐阳  阅读(205)  评论(0编辑  收藏  举报