双 Trie,怎样变成矩形计数?
P14363 · 解法三 main3.cpp · 从你已经理解的祖先关系继续
左 Trie
反转后的不变前缀右 Trie
正向的不变后缀规则矩形 · 询问点
横轴左序,纵轴右序看原字符串、拆分与全部答案
规则和有效询问一起建树。只有规则生成矩形,询问节点本身不提供贡献;不同核心组分别处理。
接回 main3.cpp:当前动作对应哪几行?
完整源码
按需补充:为什么成立,为什么比逐个检查快?
为什么是矩形?规则左终点的子树包含所有左侧匹配的询问终点,DFS 序把它变成一个连续区间;右侧同理。询问必须同时落在两个区间里,所以合法坐标恰好是两个区间的笛卡尔积,也就是一个矩形。
为什么扫描线能数对?横坐标为 x 时,只保留满足 x₁ ≤ x ≤ x₂ 的矩形。它们在各自的纵区间加一,纵坐标 y 的覆盖数就同时满足横、纵两个条件。询问只需要读取自己的 y。
为什么在 x₂+1 退出?DFS 子树是闭区间 [tin,tout],tout 这一列仍然合法。退出时刻必须在下一列;同一横坐标的更新要先于查询。
与 main.cpp 的联系。主解在左树 DFS 中维护当前祖先规则;这里给左树编号,再按横坐标维护覆盖这一列的规则。两者都先保证左条件,再统计右条件。主解沿右祖先链求和,这里给右子树区间加一后查询问点。
为什么要离线?本实现先读完规则和询问、建好树并确定 DFS 序,才能生成并排序事件。排序使横坐标条件由“进入、退出”自动维护。
复杂度。令 T=L₁+L₂。map 精确分组为 O(T log(n+1));普通 Trie 插入为 O(T),初始化和 DFS 枚举 26 个孩子;事件总数不超过 2n+q,排序与树状数组处理总计 O((n+q) log T)。固定字符集下总时间 O(T log T),空间 O(T)。当前代码组间复用节点池并清空本组使用的 BIT 范围。
查看扫描线事件顺序
点选一个事件跳到它执行之后。同 x 的更新之间顺序可交换,本页按规则编号演示;所有更新都排在查询之前。
理解检查:推演、解释、补代码
先看演示,再做题;可自由回看和重试。以下核对你选择的操作与理由,不进行 C++ 编译评测。
1 · 换一个例子,独立数覆盖
规则①的矩形是 [2,4] × [1,3],规则②是 [1,2] × [3,5]。询问点是 (4,3),答案是多少?
2 · 解释退出时刻与事件顺序
矩形横区间是 [2,4],询问恰好位于 x=4。哪种安排正确?
3 · 补全关键代码并选择理由
退出事件和同 x 排序应怎样写?
if (a.x != b.x) return a.x < b.x;
return ;