场上没调出来,写个题解纪念一下。来一个值域无关的线性做法。

统计区间数量,一个经典的套路是考虑扫描线。

具体地,我们将右端点 rr 从左往右扫,并同时对于所有 1lr1 \le l \le r 的左端点 ll,维护区间 [l,r][l, r] 能生成的所有矩形。即,我们需要维护一个若干个三元组构成的集合 SS,其中,SS 中的一个三元组 (h,w,l)(h, w, l) 代表区间 [l,r][l, r] 能够生成矩形 (h,w)(h, w)

每次 rr+1r \to r + 1 时,我们先更新 SS 内的所有三元组,丢掉不合法的,并加入 [r,r][r, r] 所对应的集合 (hr,wr,r)(h_r, w_r, r),最后令 ansans+cntans \gets ans + cnt。其中 cntcnt 为集合内本质不同的左端点个数。

考虑形式化我们要支持的具体操作:

给你两个数 H,WH, W,你要进行以下操作:

  1. 新建一个集合 SS',并将 (H,W,r)(H, W, r) 加入 SS'
  2. 对于 SS 内的每个三元组 (h,w,l)(h, w, l),你要执行以下操作:
    • h=H,w=Wh = H, w = W,将 (2h,w,l),(h,2w,l)(2h, w, l), (h, 2w, l) 加入 SS'
    • h=H,wWh = H, w \ne W,将 (h,w+W,l)(h, w + W, l) 加入 SS'
    • hH,w=Wh \ne H, w = W,将 (h+H,w,l)(h + H, w, l) 加入 SS'
    • hH,wWh \ne H, w \ne W,则什么都不做。
  3. SSS \gets S'

这个操作看起来很棘手,不好快速维护。所以我们考虑发掘一些性质;具体地,我们注意到,对于刚进行过 H,WH, W 操作得到的集合 SS 中的每一个三元组 (h,w,l)(h, w, l),一定有 h=Hh = Hw=Ww = W。于是我们维护的东西一下就从二维的形式变成一维的形式了。

我们考虑换一下维护方式,转为维护两个若干个二元组构成的集合 Sw,ShSw, Sh,并且记录 lh,lwlh, lw 分别为上次操作的 h,wh, w。类似的,其中 SwSw 中的一个二元组 (h,r)(h, r) 代表区间 [l,r][l, r] 能够生成矩形 (h,lw)(h, lw)ShSh 中的一个二元组 (w,r)(w, r) 代表区间 [l,r][l, r] 能够生成矩形 (lh,w)(lh, w)。那么我们可以直接维护了:

  1. H=lh,W=lwH = lh, W = lw。我们事先将 (H,l),(W,l)(H, l), (W, l) 分别从 Sw,ShSw, Sh 里删除,这样就可以直接分别给 Sh,SwSh, Sw 两个集合打上整体加的 tag,最后再将 (2H,l),(2W,l)(2H, l), (2W, l) 分别加入 Sw,ShSw, Sh 里即可。

  2. H=lh,WlwH = lh, W \ne lw。类似地,事先将 (W,l)(W, l)ShSh 里删除,给 ShSh 打上整体加的 tag;而对于 SwSw,我们注意到 SwSw 在操作后至多会有 O(1)O(1) 个元素,所以我们直接暴力清空 SwSw,最后再将 (2H,l),(2W,l)(2H, l), (2W, l) 分别加入 Sw,ShSw, Sh 里即可。Hlh,W=lwH \ne lh, W = lw 的情况是等价的。

  3. Hlh,WlwH \ne lh, W \ne lw。这样,Sh,SwSh, Sw 在操作后都至多会有 O(1)O(1) 个元素,所以类似地暴力清空即可。

注意到上述过程中每次只会插入 O(1)O(1) 次,删除总共 O(n)O(n) 次,均摊 O(1)O(1) 次。并且当我们要删除元素时,要么只删除 O(1)O(1) 个元素,要么只剩 O(1)O(1) 个元素,所以被我们访问到但不被删去的元素个数每次是 O(1)O(1) 的。

所以总插入删除访问次数是 O(n)O(n) 的。直接使用两个哈希表维护,同时开一个桶记录左端点为 ll 的三元组的出现次数,即可做到 O(n)O(n)。如果手写哈希表记得不要用 memset 清空 head 数组,要对于已插入的元素手动清空。

code