hdu 4302 《Holedox Eating》



这个题目告诉我, 想东西的时候不要那么死板. 要发散, 只有一个规则, 就是没有规则
这题可以用线段树做, 设计节点的附加信息时, 其实只要一个sum, 表示该区间的蛋糕总数. 为什么这么设计就可以了呢???


考虑我们需要得到的东西, 其实是某个区间的最右边(或者最左边, 原理一样)的蛋糕的位置. 我们从根出发, 每次都可以二分, 我们先看右孩子的sum是不是非0, 如果是非0的话, 那么我们就可以在右孩子那里继续找,不然,我们就在左孩子那里找。由于每次都总是进入一个分支而已,所以复杂度是logn的。至于修改, 就是简单的单点修改而已。


除了线段树, 这道题还可以用一般的bst做。 我们可以建立一棵bst。每出现一个cake,就把cake的位置插进去。当我们要吃最近的cake的时候,实际上就是在bst里面找出两个数:
大于当前位置pos的最小数
小于当前位置pos的最大数


怎么找呢,同样是分治的做法,假设现在的根是cur,我们用cur和pos做对比, 从而确定我们要进入哪个孩子。


除了二叉树, 这道题还可以用两个堆, 一个最大堆h1, 一个最小堆h2
写一个程序, 保持这样的一个性质:
h1总是放着当前位置左边的那些cake的位置

h2总是放着当前位置右边的那些cake的位置




#include <cstdio>
using namespace std;
struct _Node {
  int max, min;
  int num;
  _Node * left, * right;
};

typedef _Node * NodePtr;
_Node __nodes[100005 * 2];
int __top;


NodePtr newNode() {
  return &(__nodes[__top ++]);
}




NodePtr makeTree(int a, int z) {
  if (a == z) {
    NodePtr cur = newNode();
    cur->left = cur->right = NULL;
    cur->max = cur->min = -1;
    cur->num = 0;
    return cur;
  }

  int mid = a + (z - a) / 2;
  NodePtr cur = newNode();

  cur->left = makeTree(a, mid);
  cur->right = makeTree(mid + 1, z);
  cur->max = cur->min = -1;
  cur->num = 0;

  return cur;
}


void change(NodePtr cur, int a, int z, int pos, int val) {
  if (a == z) {
    cur->num += val;
    if (cur->num > 0) {
      cur->max = cur->min = a;
    }
    return;
  }

  int mid = a + (z - a) / 2;
  if (pos <= mid) change(cur->left, a, mid, pos, val);
  if (pos > mid) change(cur->right, mid + 1, z, pos, val);

  if (cur->right->num != 0)
    cur->max = cur->right->max;
  else if (cur->left->num != 0)
    cur->max = cur->left->max;
  else
    cur->num = 0;


  if (cur->left->num != 0)
    cur->min = cur->left->min;
  else if (cur->right->num != 0)
    cur->min = cur->right->min;
  else
    cur->num = 0;



  cur->num = cur->left->num + cur->right->num;
  return;

}

bool hasCommon(int a, int b, int c, int d) {
  return !(b < c || d < a);
}



int queryMax(NodePtr cur, int a, int z, int start, int end) {
  if (start <= a && z <= end) {
    if (cur->num == 0)
      return -1;
    else
      return cur->max;
  }

  int mid = a + (z - a) / 2;
  int res = -1;
  if (hasCommon(mid + 1, z, start, end)) {
    res = queryMax(cur->right, mid + 1, z, start, end);
  }
  if (res == -1) {
    if (hasCommon(a, mid, start, end))
      res = queryMax(cur->left, a, mid, start, end);
  }

  return res;
}

int queryMin(NodePtr cur, int a, int z, int start, int end) {
  if (start <= a && z <= end) {
    if (cur->num == 0)
      return -1;
    else
      return cur->min;
  }

  int mid = a + (z - a) / 2;
  int res = -1;
  if (hasCommon(a, mid, start, end)) {
    res = queryMin(cur->left, a, mid, start, end);
  }
  if (res == -1) {
    if (hasCommon(mid + 1, z, start, end))
      res = queryMin(cur->right, mid + 1, z , start, end);
  }

  return res;
}


int L;
int N;
NodePtr root;
int curPos;
int curDirection = 1;

int curCase;

void run() {


  __top = 0;
  scanf("%d %d", &L, &N);
  root = makeTree(0, L);
  int i;
  int event;


  curPos = 0;

  int total = 0;
  for (i = 1; i <= N; ++i) {
    scanf("%d", &event);
    if (event == 0) {
      int pos;
      scanf("%d", &pos);
      change(root, 0, L, pos, 1);
    } else {
      int resLeft = queryMax(root, 0, L, 0, curPos);
      int resRight = queryMin(root, 0, L, curPos, L);

      //printf("$ %d %d\n", resLeft, resRight);

      if (resLeft == -1 && resRight != -1) {
        total += ( resRight - curPos);
        if ( resRight - curPos != 0) curDirection = 1;
        curPos = resRight;

        //printf("1 eat %d\n", curPos);
        change(root, 0, L, curPos, -1);

      } else if (resLeft != -1 && resRight == -1) {
        total += ( curPos - resLeft);
        if (curPos != resLeft) curDirection = -1;
        curPos = resLeft;
        //printf("2 eat %d\n", curPos);
        change(root, 0, L, curPos, -1);
      } else if (resLeft != -1 && resRight != -1) {
        if (curPos - resLeft == resRight - curPos) {
          if (curDirection == 1) {
            total +=( resRight - curPos);
            if ( resRight - curPos != 0) curDirection = 1;
            curPos = resRight;
            //printf("3 eat %d\n", curPos);
            change(root, 0, L, curPos, -1);

          } else {
            total +=( curPos - resLeft);
            if (curPos != resLeft) curDirection = -1;
            curPos = resLeft;
            //printf("4 eat %d\n", curPos);
            change(root, 0, L, curPos, -1);
          }
        } else if (curPos - resLeft < resRight - curPos) {
          total +=( curPos - resLeft);
          if (curPos != resLeft) curDirection = -1;
          curPos = resLeft;
          //printf("5 eat %d\n", curPos);
          change(root, 0, L, curPos, -1);
        } else {
          total +=( resRight - curPos);
          if ( resRight - curPos != 0) curDirection = 1;
          curPos = resRight;
          //printf("6 eat %d\n", curPos);
          change(root, 0, L, curPos, -1);
        }
      }
    }
  }


  printf("Case %d: %d\n", curCase, total);


}



int main() {
  int T;
  scanf("%d", &T);
  for (curCase =  1; curCase <= T; ++curCase)
    run();
  return 0;
}

转载于:https://my.oschina.net/mustang/blog/98399

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值