我正在尝试解决定义如下的问题:
糖果由从 1 到 1e6 的数字表示。如果一个人有 1 到 k 个糖果,则称他有 k 个糖果。
例如,如果一个人买了糖果 1、2 和 6,那么他有一套 2 颗糖果
给定 2 种类型的操作:吃 x 和买 x,其中 x 代表糖果数量。购买 x 只会将 x 的数量增加 1。吃 x 只会使 x 的数量减少 1。
对于每个操作,回答问题,我现在拥有的那套糖果的大小是多少?
我正在努力寻找最有效的方法来做到这一点。我想到的解决方案描述如下:
让 count[i] 定义大小为 1 - N 的数组,其中 N 是可能的最大糖果数。count[i] 存储我目前拥有的编号为 i 的糖果的数量。
让 Fenwick[i] 数组大小为 1 - N,其中 N 是最大可能的糖果数。这个数组用于构建一个 fenwick 树来存储我收藏中糖果的累积总和。此累积和不使用计数数组。累积总和计数 1 的数量(每个 1 表示我的收藏中存在糖果 x)。例如,如果我有一组 5 颗糖果,那么从 1 到 5 的累积总和是 5。如果有一组 10 颗糖果,那么从 1 到 10 的累积总和是 10...
对于购买操作,如果我的收藏中还没有糖果 x,则从索引 x 开始的累积总和加 1(这由 fenwick 树处理)。否则,我将只执行 count[x]++
对于eat 操作,执行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))