Skip to content

交付一份保留查询身份的批量统计记录 ​

批量查询的重排与重放路线在这里把四种方法接到真实输入。下载标准库核验器和完整运行结果,运行python algorithms-batched-range-check.py。程序无网络依赖,在脚本同目录写出JSON;python -O仍执行全部显式检查。

交付结果须包含每条查询的原编号、实际执行次序、维护的不变量和使用的输入条件。朴素扫描可以核答案,但不能替代对“为什么允许重排”的解释。以下数组均从下标0开始,区间[l,r)不包含r。

一、先在线回答,再保存同一份事件 ​

初态A=[3,1,3,2,1,4,2,3],取B=3,初始排序块为[1,3,3]、[1,2,4]、[2,3]。按下列顺序到达请求,每个count都要在下一条请求到来前回答:

命令时刻 请求 立即返回
1 count(1,7,2) 4
2 set(2,1) 无查询输出
3 count(0,4,1) 2
4 set(5,1) 无查询输出
5 count(2,8,2) 5
6 set(2,1) 无查询输出
7 count(4,4,99) 0

第一条查询分成[1,3)、整块[3,6)、[6,7),贡献1、2、1。第一次赋值后,第一排序块只删掉一个3并插入一个1,成为[1,1,3];第二次赋值后,中块成为[1,1,2]。第六条给位置2重复赋相同值,不改变后续计数。

自己运行SortedBlocks并在每次赋值后调用audit(),确认排序块恰好是对应原位置的多重集合。该检查会重排并比较整份数组,是测试成本;在线核心的更新只移动所属块内的元素。

二、把点赋值转成可撤销的贡献 ​

现在整份事件已经保存,可以用CDQ批处理同一问题。初态8个位置各产生一条(0,i+1,A[i],+1)。三条赋值各产生两条记录:旧值−1、新值+1。因此共有14条更新记录。

每个区间count(l,r,k)拆成两个阈值查询H(t,r,k)与H(t,l,k),相减恢复结果,四条count共有8条前缀查询。第二维是i+1,所以不能把减项误写成l−1。即使空区间,两个前缀查询仍可正常生成并相消。

先手算时刻3、阈值1、前4个位置:初态位置1贡献1;时刻2把位置2的旧3删去、新1加入,新增贡献1,总计2。到时刻5、区间[2,8)、阈值2,共保留位置2、3、4、5、6五项。

调用assignment_counts(A, operations)后,JSON中assignment.answers应为[4,2,5,0],并列出14条更新、8条前缀查询。核验器还记录本次分治实际22次树状数组加入、22次撤销;同一更新可能在不同层参与不同查询,不能把这个执行次数误当成输入更新数。

另检查带重复坐标的独立例子:更新(0,1,2,+3)、(0,1,2,−1)、(1,0,2,+5)、(0,2,1,+7),两个阈值(0,1,2)、(1,2,2)答案2、14。根结点分别贡献2、9,右子树给第二个阈值补5。若删除负权、去重更新或把某个≤改成<,至少一个结果会变化。

三、冻结初态,用一个窗口回答七个请求 ​

回到未经任何赋值的初态A。七个查询按输入编号依次为[2,7)、[0,4)、[4,8)、[1,6)、[3,3)、[0,8)、[3,5)。每份答案包含不同值数D与频次平方和F。

使用B=3的Mo次序为1、3、0、5、6、2;空查询4直接得到(0,0)。按原编号交付的结果为

[(4,7), (3,6), (4,4), (4,7), (0,0), (4,18), (2,2)]。

当前窗从[2,7)去[0,8),先加入下标1、0,再加入7。全数组中3出现3次,1、2各2次,4出现1次,故F=18。下一窗[3,5)要删去左侧0、1、2和右侧7、6、5;每次删除前该位置都仍在窗口中。

本次移动统计为左加2、右加11、左删6、右删3,共22个单位置转移。JSON的trace保存每次查询前后的窗口及两项统计;答案数组仍按0至6排列。逐步检查频次非负和频次总和=R−L,不只看最后的F。

从空窗[0,0)直接转到[4,6)是另一个必要手算:先加入0到5,再删0到3。若先删除左边界,就在第一步删除未出现元素。对于长度5、F=7的查询0,同值无序位置对数为(F−5)/2=1,抽两个不同位置相等的概率1/10;空窗不能使用该概率公式。

四、为日期二分证明单调性 ​

另取A₀=[0,0,0,0]。六天增量依次为(1,+2)、(3,+3)、(0,+1)、(2,+4)、(1,+0)、(3,+2)。查询依次为:

编号 固定区间 目标和 首次日期
0 [0,2) 3 3
1 [2,4) 5 4
2 [0,4) 0 0
3 [1,3) 7 不存在
4 [0,1) 2 不存在
5 [2,2) 1 不存在

每个位置只增加,所以任何固定区间的和不下降;每条“至少达到目标”谓词先假后真。这才允许二分,与CDQ中合法的负权抵消是不同条件。

首轮所有查询的边界为(-1,7),中点都为3;一份日期3状态同时回答六项。第二轮查询0、2的中点为1,其余为5。从初态重放一遍,在日期1读[0,2,0,0],在日期5读[1,2,4,3],即可完成两桶;不为每个查询重新执行五条更新。

第三轮查询2检查日期0并返回0。完整最终lo=[2,3,−1,6,6,6],hi=[3,4,0,7,7,7];7只是真哨兵,映射成JSON的null。实际三轮共18次更新、18次谓词检查,初态重建累计12个数组单元。

五、改变结构条件,选择还能成立的接口 ​

  1. 将事件的下一条真实查询改为依赖上条答案才能解码。排序块仍能逐条回答;Mo与CDQ不能预先排序未知坐标。仅有文件里的编码串不足以满足离线条件。
  2. 在静态Mo七查询之间加入set(2,1)。不能继续用原静态窗口答案;要保留在线次序或另外设计带时间的状态。当前下载器的Mo接口没有修改参数。
  3. 用单个“最小值”替代Mo的频次数组。删除唯一最小值时信息不足;请给出[1,4,7]与[1,5,7]这个相同旧摘要、不同新答案的反例,再决定补何种结构及其成本。
  4. 单元素初态0,更新改成+5、−5、0、0,目标3。实际首次日期为1,但日期序列是假、真、假、假、假;首轮中点2判假已删掉正确日期。批量二分接口应拒绝负增量,不能继续声称首真证明成立。
  5. 把数组清空。分块只允许[0,0)查询,Mo给所有空窗(D,F)=(0,0),CDQ零更新的前缀都为零,批量二分的空区间只在目标≤0时返回日期0。任何点更新都没有合法位置。

六、验收记录的成本栏 ​

排序块的计数成本为O(B+(n/B)log(B+1)),赋值O(B);只有常数大小摘要可直接合并时,才是常见的O(B+n/B)查询。Mo除指针项qB+n²/B外,还要计查询排序、值压缩和输出q个结果。

CDQ的O(N log²(N+1))中,N是变换后的更新与前缀查询记录数,不能只按原数组长度报数;同坐标记录不去重。核心线性空间要求不保存每层历史,关闭逐结点整树扫描audit。批量二分每轮都要重建n项初态并清桶,不能只数树状数组操作。

下载器默认核心关闭audit;main为了检验每个窗口与日期,显式打开较慢的朴素核对。这些测试遍历不属于正文核心渐近界。所有整数算法的单位成本界还要求数值、下标和累计和能放入机器字;Python不溢出不代表大整数位运算免费。

最终交付JSON、两条事件编码的手算、七个查询的原ID答案、批量二分三轮桶记录,以及五项结构迁移的解释。有限穷举核对的是实现;正文的不变量、唯一贡献归属与单调性分别承担一般正确性。