0%

CF-Edu-186-F1. Christmas Reindeer (easy version/hard version)

思路讲解

这是问题的简单版本。版本之间唯一的区别在于nnmm的上限。在这个版本中,n500n \le 500m500m \le 500

你有一群nn只圣诞驯鹿。第ii只驯鹿的力量是2ci2^{c_i}

kk只圣诞驯鹿组的运载能力计算如下:

  • 驯鹿的力量按非递增顺序排序。我们把排序后的力量列表表示为c1,c2,,ckc'_1, c'_2, \dots, c'_k,其中cici+1c'_i\ge c'_{i+1}

  • 然后,这群驯鹿的运载能力等于c1+c22+c34++ck2k1c'_1 + \lfloor\frac{c'_2} {2}\rfloor + \lfloor\frac{c'_3} {4}\rfloor + \dots + \lfloor\frac{c'_k} {2^{k - 1}}\rfloor

注意,一些驯鹿可能对群体的运载能力没有贡献。

你需要处理三种类型的查询:

  1. 添加一只力量为2x2^x的驯鹿到群中;

  2. 从驯鹿群中移除一只力量为2x2^x的驯鹿;

  3. 计算选择一些驯鹿(可能是所有)的方式数量,使得所选群体的运载能力至少为xx

如果群中有多只力量相同的驯鹿,它们被视为不同的驯鹿。例如,如果你有两只力量各为11的驯鹿,并且需要计算选择运载能力至少为11的群体的方式数量,那么有33种选择方式:选择第一只驯鹿,选择第二只驯鹿,或者选择它们两只。


思路讲解

这个全部都和 2 的次方有关系,我们不自觉的就会往二进制的方向想,但是问题在于,我们如何处理这个进位问题?

等等,有没有一种可能,我们不需要处理进位问题c1+c22+c34++ck2k1c'_1 + \lfloor\frac{c'_2}{2}\rfloor + \lfloor\frac{c'_3}{4}\rfloor + \dots + \lfloor\frac{c'_k}{2^{k - 1}}\rfloor,由于这个 C 是非严格递减的,即便在全部相等的前提下,因为右移作用越来越强烈,其为 1 的地方也在不断往后移动,因此,其实不会发生进位。

那么既然不会发生进位,第一个堵点就消失了。

接着我们就手玩一下样例,用组合计数的方式解决一下这个问题。

注意,在设计这个计数算法的过程当中,尽量让每层保持独立,即该层只考虑选了该层的数字的情况,这样子前面的数字因为选了他们的组合已经选过了,就不用考虑了,会轻松一点。

不过这道题目能不能做到这一点呢?就让我们看看解法吧。(是可以的,但需要一个关键观察,特别是对于前缀相等情况)

为了方便描述这个过程,令 freqifreq_i 为输入数组中 i 的频数,preipre_i 为 i 位置前(包括自己)最晚的 1 出现位置(描述的是查询数字 x),remrem 这个数字指的是 i 之后所有的数字的频数之和。

答案 ans0ans\gets0,等于(前缀相同)情况下的辅助统计量 eqAns1eqAns\gets1

其实,在做这道题目的时候,我也不止一次的想过,要是这道题目是 >x>x 就好了。那所以说,我们为什么不这么做呢?eqAnseqAns 维护等于情况,ansans 维护严格大于情况,最后 ansans+eqAnsans\gets ans+eqAns 不就行了?

从高到低遍历,遇到一位 freqi>0freq_i>0,有这么几种情况。

  • Case 1:i=preii=pre_i
    • 如果 freqi=1freq_i=1,不需要进行任何操作。
    • 如果 freqi>1freq_i>1,这个是最复杂的情况。面对这种情况,我还想过记忆化搜索,dp 等等,以解决这个问题,不过最后发现,其实都不用,只需要一个关键观察:即需要选择多少个 i 达到这个等于状态是唯一确定的。需要选择的数量,就是(自这位开始的)连续 1 的数量,令其为 conti1\text{conti1}
      • 首先,如果你选择的数量超过了(自这位开始的)连续 1 的数量,那么你就已经超过了,超过的时候至少要驻守 conti1+1\text{conti1}+1 个,才能保证超过,当然,除了 iconti1=0i-\text{conti1}=0 的特殊情况,这种特殊情况因为相等,所以只能更新 eqAnseqAns***。***当然,写出这里的统计式子需要有这么一个想法,就是把 remrem 和这个前面这部分分开来看。

iconti1=0:eqAnseqAns×(j=contifreqiCfreqij)×2remiconti1>0:ansans+eqAns×(j=conti+1freqiCfreqij)×2remeqAnseqAns×Cfreqiconti1i-\text{conti1}=0:eqAns\gets eqAns\times(\sum^{freq_i}_{j=\text{conti}}C_{freq_i}^{j})\times2^{rem} \\ i-\text{conti1}>0:ans\gets ans+eqAns\times(\sum^{freq_i}_{j=\text{conti+1}}C_{freq_i}^{j})\times2^{rem} \\ eqAns\gets eqAns \times C^{conti1}_{freq_i}

  因为 $\text{conti1}$ 很小,这个式子中间的求和可以用从全组合减去东西的方式优化,达到 $O(W) $ 级别。
- 那么,你如果选的数量小于连续 1 的数量,那么你直接就没有资格相等(以第一个样例为例,$2\ 1\ 1$,只能凑出 $5={(101)}_{2}$,没法凑出 $6={(110)}_{2}$ 这样连续的 1,因为 2 的数量没有达到这个连续段 1 的数量要求)。因此如果 i 的频数小于连续的 1 的数量,直接 break 整个计数过程,并且使得  $eqAns\gets 0$。
- 当然,我们还需要进行**全局右移操作**,即:
1
2
3
4
5
6
auto right_move=[&](ll step) {   //  传入参数 step = conti1
for (int j=1;j<i;++j) {
freq[max(0ll,j-step)]+=freq[j];
freq[j]=0;
}
};
  • Case 2:i>preii>pre_i
    注意,思路要清楚,ansans 一定要保证这个答案 >x>xeqAnseqAns (统计的)一定要保证前缀严格相等。还是一样,i=0i=0 代表运载能力为 0,只能更新 eqAnseqAnsi>0i>0 的时候更新 ansansj=1j=1 开始是因为至少你要选一个才超过是吧。

i=0:eqAnseqAns×2freqii>0:ansans+eqAns×(j=1freqiCfreqij)×2remi=0:eqAns\gets eqAns \times 2^{freq_i} \\ i>0:ans\leftarrow ans+eqAns\times(\sum^{freq_i}_{j=1}C_{freq_i}^{j})\times2^{rem}

如果说 freqi=0freq_i=0,需要进行这个合法性检验,即如果 i=preii=pre_i,直接 break 整个计数过程,并且使得 eqAns0eqAns\gets 0

⚠️ 注意:我们从高到低遍历,我们要遍历到 -1(就是代表这些驼鹿的运载能力归零了),因此,我们会给整个系统加一个偏移量,0 代表指数为 -1,1 代表为 0,以此类推。

⚠️ 注意:所有的 break 之前,都要使得 eqAns0eqAns\gets 0

AC代码

AC,解法可以通过 F2。

https://codeforces.com/contest/2182/submission/357349404


心路历程(WA,TLE,MLE……)

一开始的算法得出来的答案不太对,因为计数条件和题目规定有不同,题目的 C 数组是排好序的,因此就不在乎顺序了,求的就是组合数。

image

image

  • 如果 freqi>1freq_i>1,还好办吗?我们先想一种,即 ansans+2rem×freqians\leftarrow ans+2^{rem}\times freq_i,毕竟有 freqifreq_i 种选择嘛,这种自然是没什么问题。但是,选了一个 i 以后,其他的 i 我们直接放任不管吗?这是不行的,我们还需要进行右移操作,即 freqi1freqi1+freqi1freq_{i-1}\gets freq_{i-1}+freq_i-1(先右移,再统计这个 remrem 数量)。

  • 如果 freqi>1freq_i>1,这个是最复杂的情况,我们需要求出来新的 eqAnseqAnseqAnseqAns×freqieqAns\gets eqAns \times freq_i,然后,我们要进行右移操作,我们进行的是全局右移操作,即对  i<i\forall\ i'< ifreqi1freqi1+freqifreq_{i'-1}\gets freq_{i'-1}+freq_{i'}freqi1freqi1+freqi1freq_{i-1}\gets freq_{i-1}+freq_i-1

image

  • Case 2:i=preii=pre_i
    • 如果 freqi=1freq_i=1,不需要进行任何操作。
    • 如果 freqi>1freq_i>1,这个是最复杂的情况。我们发现,这里,如果我们选了第一个数,可以下放 freqi1freq_{i}-1 个数字到 freqi1freq_{i-1},但是选了第二个数字以后,只能下放 freqi2freq_{i}-2 个数字到 freqi1freq_{i-1},这种下放,好像不能用公式法做出来,难道每一个都要重新算吗?
      • 其实我们会有一个感觉,就是好像,这个是可以记忆化搜索,或者说是 dp 的。可以发现我们的这个状态由这几个因素决定:
        image
      • 其实我们会发现这个 dp 和询问的 x 没什么关系,可以放在一起做?不过你说还有操作增删操作?呃,这个确实是一个问题,不过由于整个东西是离线的,我们可以得到每个位置的 freqifreq_i 的最大值,按照这个 dp,就不会出现问题了。
      • 不过还有一个问题,就是如何将查询做到 O(1)O(1) 呢?其实这个的话前缀和即可。不过折腾来,

⚠️ 注意:注意有限右移操作和全局右移操作。

那么这个上面是我们脑内模拟了一下这个过程,得出来的计数结论,我们实现一下,看看对不对?

哈哈,我们会发现,这样子操作,得出来的答案不对,这是为什么呢?

以这个第一个样例的第一个询问为例,我们会发现,其实这个答案并不在乎顺序

那我们的计算要怎么样搞?其实,我们发现我们上面的思路还是比较混乱的,即有的时候认为是组合,有的时候又认为是排列。

不过,我们发现,题目中的 cici+1c'_i\ge c'_{i+1} 是非常重要的,这意味着,只要是为了这个相等作用,无论

⚠️ 注意:我们从高到低遍历,我们要遍历到 -1(就是代表这些驼鹿的运载能力归零了),因此,我们会给整个系统加一个偏移量。

rem=0:ansans+eqAns×((2rem+freqiconti1++2rem)+(2rem+freqiconti11++2rem)++2rem)rem0:ansans+eqAns×((2rem+freqiconti11++2rem)+(2rem+freqiconti12++2rem)++2rem)rem=0:ans\gets ans+eqAns\times((2^{rem+freq_{i}-\text{conti1}}+\cdots+2^{rem})+ \\ (2^{rem+freq_{i}-\text{conti1}-1}+\cdots+2^{rem})+\cdots+2^{rem}) \\ rem≠0:ans\gets ans+eqAns\times((2^{rem+freq_{i}-\text{conti1}-1}+\cdots+2^{rem})+ \\ (2^{rem+freq_{i}-\text{conti1}-2}+\cdots+2^{rem})+\cdots+2^{rem})

  • Case 1:i>MSBi>\text{MSB}(MSB 即查询数字 x 最大二进制位的位数)
    • 如果 freqi=1freq_i=1,那么好办,直接 ansans+2remans\leftarrow ans+2^{rem} 即可。
    • 如果 freqi>1freq_i>1,还好办吗?这里需要有这么一个想法,就是把 remrem 和这个前面这部分分开来看,即:

ansans+2rem+freqi1+2rem+freqi2++2remans\leftarrow ans+2^{rem+freq_{i}-1}+2^{rem+freq_{i}-2}+ \cdots+ 2^{rem}

这个式子这样子理解,首先,这个数组 C 是倒序排序的,即 $c'_i\ge c'_{i+1}$,因此,选了排在第一个的数,就有 $2^{rem+freq_{i}-1}$ 种组合(全组合的这个公式),选了排在第二个的数,就只有 $2^{rem+freq_{i}-2}$ 种组合了(没法选第一个数),一次类推,就得到了这个式子。之后,我们就当 i 这个数字不存在就行了。