这个题目告诉我, 想东西的时候不要那么死板. 要发散, 只有一个规则, 就是没有规则
这题可以用线段树做, 设计节点的附加信息时, 其实只要一个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;
}