计算连续糖果的数量

时间:2016-10-23 12:28:23

标签: algorithm fenwick-tree

我正在尝试解决定义如下的问题:

  

糖果由1到1e6的数字表示。一个人   如果他有1到k的糖果,据说有一套k糖果。

     

E.g。如果一个人买了糖果1,2和6,那么他有一套2   糖果

     

给出两种类型的操作:吃x和买x,其中x代表   糖果号码。购买x只会将x的数量增加1。吃x   将x的数量减少1。

     

对于每个操作,回答问题,我现在拥有的那套糖果的大小是多少?

我正在努力寻找最有效的方法。我想到的解决方案如下所述:

让count [i]定义大小为1 - N的数组,其中N是最大可能的糖果数。 count [i]存储到目前为止我用的数字i的糖果数量。

让Fenwick [i]数组大小为1 - N,其中N是最大可能的糖果数。此数组用于构建fenwick树,以存储我的集合中的累积糖果总和。此累积和不使用计数数组。累计总和计数1s的数量(每个1表示我的收藏中存在糖果x)。例如如果我有一套5个糖果,那么从1到5的累积总和是5.如果有一组10个糖果,那么从1到10的累积总和是10 ...

对于购买操作,如果糖果x不在我的集合中,则从索引x开始向累积金额添加1(这由fenwick树处理)。否则,我将执行count [x] ++

对于吃操作,执行count [x] - 。如果count [x]现在为0,那么我从索引x开始的累计和中减1(这由fenwick树处理)。

现在解决了插入和删除的部分。困难的部分是如何获得当前集合中糖果的大小。

我试图在Fenwick树中查询最大的索引i,其中从1到i的累积和等于i,同时每次以2的幂递增查询索引。

我把最大的索引作为一组有效的糖果,j和最小的索引,这是一个无效的糖果集合,k。然后从j循环到k,在每次迭代时查询fenwick树。一旦循环遇到无效的集合,就打破并输出答案。

在我看来,这会奏效。但是,这当然不是一种有效的方法。有人能够启发我更好的解决方案吗?提前谢谢。

编辑(解决方案):

我的插入和删除方法是正确的。只是我正在以不正确的方式搜索糖果的集合。在这种情况下,我们想要最大数x,其中query(x)= x(query(x)给出从1到x的累积和)。所以我们可以使用二进制搜索来找到x的最大有效值(query(x)= x)。为了实现这一点,我们只需要保留一个额外的变量来跟踪x的最后一个值,其中query(x)给出了一个有效的集合。

溶液的复杂性:O(log ^ 2(N))

1 个答案:

答案 0 :(得分:1)

这通常是二叉树结构。

为简单起见,我们假设某些整数0的糖果指数从2^k - 1k不等。然后,每个时刻的状态都由16个数字c[0]代表c[2^k - 1]来表示,其中c[i]是糖果数量i

我们按如下方式构造二叉树:根节点P(0, 2^k)表示整个区间[0, 2^k);对于P(a, b)这样的每个节点b - a > 1,构建两个子节点P(a, (a + b)/2)P((a + b)/2, b)

p(a, b)成为c[i]区间i [a, b)的最小值p(a, a + 1) = c[a]。显然我们有:

  • p(a, b) = min{p(a, (a + b)/2), p((a + b)/2, b)};

  • b - a > 1 if O(k)

构建了这个数据结构后,对于每个操作(加上一个或减一个),我们可以按O(k)步骤从下到上更新数据。此外,找到糖果组的大小也可以在k = 3步骤中完成。

数据结构示例:

让我们看看案例c[0],以便有c[7]c[0 .. 7] = {1, 3, 0, 4, 3, 2, 8, 1}。例如:

p(0, 8) = 0 |- p(0, 4) = 0 | |- p(0, 2) = 1 | | |- p(0, 1) = 1 | | |_ p(1, 2) = 3 | |_ p(2, 4) = 0 | |- p(2, 3) = 0 | |_ p(3, 4) = 4 |_ p(4, 8) = 1 |- p(4, 6) = 2 | |- p(4, 5) = 3 | |_ p(5, 6) = 2 |_ p(6, 8) = 1 |- p(6, 7) = 8 |_ p(7, 8) = 1

树结构如下所示:

1

现在假设我们将c[2]添加到1的数字p(2, 3),然后我们只需更新数字p(2, 4)p(0, 4)p(0, 8)basename