基础子串结构
大概阅读完了。那么写一些要点。
从这个更加机械的角度思考,可能更容易将一些问题做到更优的复杂度。
理论
- 等价类:
- ext 相同则在同一个等价类中。ext 是保证 occ 相同的最大拓展串。
- 等价类的形状:倒阶梯型。
- 代表元:ext,即左上元素。
- 出现次数为 occ,每个等价类的周长总和为 \(O(n)\)。
- parent tree 相关:
- 正 SAM 上节点与等价类中的行一一对应。反 SAM 也与列有一一对应关系。
- 正 SAM fail 边若规定从父亲连向儿子,则应该是一行的左侧连向另一行的右侧,其中这两行在平面上是相邻的。反 SAM 同理,从上侧连向下侧。
- DAG 相关:
- DAG 边的 \(+c\) 操作可以描述为反 SAM 的跳 parent tree 儿子操作。
构建
- 先建立 正/反 SAM。
- 找到代表元:
- 代表元性质:一定是正/反节点内最长的串。
- 我们在 正 SAM 上走 DAG 边,要求保证当前串是节点内最长的。同时,维护 反 SAM 上所在的节点,这个我们预处理每个节点前加入 \(c\) 字符会到哪个儿子即可。
- 给所有行列找到代表元:
- 对于不是等价类中第一行/列的节点,他们一定只有一条 DAG 出边,且指向它的上一行/列。
- 我们给他编个号,要求处理到当前节点的时候,出边的代表元已经确定。
- 事实上根据 SAM 节点编号从大往小扫就好了。
- 给等价类内部行列排序:直接按照节点编号就是有序的。
例题
居然在这里找到了模拟赛题目?!?!
首先给条件做个基本的转化:所有的 \(a,b\) 只能在 \(acb\) 中出现,\(c\) 唯一。
即 \(a,b,acb\) 在同一个等价类中。
考虑按照 \(acb\) 计数,在等价类中从下往上扫行,维护一些前缀和的信息+双指针即可计算。
更详细的做法在这里
可以求出每个 \(wl_i\) 的贡献。
首先考虑等价类中子串 \(s_{x,y}\) 的贡献?他是对应行列的 \((\Sigma vl)\times (\Sigma vr)\times \mathsf{occ}\)。
也就是我们可以先处理出 \(\Sigma vr,\mathsf{occ}\),然后扫列,前缀和+双指针即可维护每个 \((\Sigma vl)_i\) 的系数,然后 pushdown 就好了。
就是你从 \((1,n)\) 往右下走,你经过的格子的贡献是它的 \(\mathsf {occ}\),我们走到一个 \((x,x)\) 就停止。
将路径方向改成 \((x,x)\to (1,n)\)。
考虑一个等价类中,走到第一行/列的方案数如何算?首先将等价类所有的入口(等价类外边,往左/上走一步可以到达等价类内部)找出来,设他们是 \((a_i,b_i,f_i)\),\(f_i\) 表示 dp 值,然后 \(a,b\) 是这个点到右上角的距离。
对于到第一行的,然后设个 GF:
由于 \(a_i,b_i\) 同增,可以分治计算。列同理,总复杂度 \(O(n\log ^2 n)\)。
将问题放到二维平面,这是一个二维数点问题。
对于同一个等价类内部的询问,我们可以直接二位数点处理。
对于外部的,可以差分成求:行/列的祖先和、右侧一个代表元的答案。
复杂度 \(1\log\)
浙公网安备 33010602011771号