【笔记】子序列问题
对于序列 \(a(n)\),我们称 \(b(m)\) 为 \(a\) 的子序列,当且仅当存在 \(c(m)\) 使得 \(\forall 1\le i<m,c_i< c_{i + 1}\) 并且 \(\forall 1\le i \le m, a_{c_i} = b_i\)。
一个序列的子序列有 \(2^n\) 种,非空子序列有 \(2^n - 1\) 种。
子序列的子序列有 \(3^n\) 种,子序列的子序列的子序列有 \(4^n\) 种,依次类推。
[Ynoi2015] 盼君勿忘
每次询问一个序列所有子序列去重后的权值和之和。
考虑对每个权值算贡献,即有多少个子序列包含这个权值,简单容斥下就是 \(2^n-2^{cnt}\) 个,所以贡献就是 \((2^n-2^{cnt})x\)。
例题
给定一个子序列,求有多少个本质不同子序列。
定义 \(f_i\) 表示以 \(i\) 结尾的本质不同子序列数,显然有 \(f_i = \sum f_j\),其中 \(s_j\) 是在 \(i\) 前面第一次出现。直接转移可以做到 \(\mathcal{O}(|S|\sum)\)。
事实上我们有更优的容斥方法,定义状态 \(f_i\) 表示前 \(i\) 个位置有多少个本质不同的子序列数。
考虑从 \(f_i \to f_{i + 1}\),显然第 \(i + 1\) 位可选可不选,所以 \(f_{i + 1} = 2\times f_i - f_{pre_{i+ 1} - 1}\),其中 \(pre_i\) 表示 \(i\) 前面第一个和 \(S_i\) 相同的位置。时间复杂度为 \(\mathcal{O}(|S|)\)。
【模板】子序列自动机
子序列自动机相对于子序列,好比后缀自动机相对与字符串,回文自动机相对与回文串。
我们观察例题的两种方法,找到二者的本质相同之处。
不难发现如果我们对于所有 \(i<j\) 使得 \(s_j\) 是在 \(i\) 之后第一次出现,连一条 \(i\to j\) 的边,那么我们就会得到一个有向无环图,即子序列自动机。
所以自动机中的一条路径与一个子序列一一对应,本质不同的子序列数就是路径数。
对于例题的第一种方法,\(f_i\) 表示以 \(i\) 为终点了路径数,对于第二种方法表示前 \(i\) 个点的路径数,这样就很清晰是正确的。
例2 Distinct Subsequences
求所有子序列的本质不同子序列个数之和。
显然就是求所有子序列的子序列自动机中路径条数。
所以我们可以定义状态 \(f_i\) 表示以 \(i\) 结尾的所有子序列中,所有以 \(i\) 结尾的路径之和,那么 \(i\) 之后任意选,答案为 \(\sum 2^{n - i}f_i\)。
我们枚举 \(j\) 进行转移,其中区间 \([j + 1,i - 1]\) 中等于 \(s_i\) 的位置不能选,其余位置任选,所以 \(f_i = \sum f_j2^{cnt_j}\),\(cnt_j\) 表示区间 \([j + 1, i - 1]\) 中不等于 \(s_i\) 的位置。
直接转移可以做到 \(\mathcal{O}(n^2)\),事实上我们发现在从小到大枚举 \(i\) 的过程中,对于 \(i\) 后面的 \(=s_i\) 的位置,相当于统一 \(/2\),打个标记即可。这样就可以做到线性。