我正在尝试解决定义如下的问题:
糖果由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))
答案 0 :(得分:1)
这通常是二叉树结构。
为简单起见,我们假设某些整数0
的糖果指数从2^k - 1
到k
不等。然后,每个时刻的状态都由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
。