D - Ulam-Warburton Automaton
待补。
E - Count Sequences 2
多重集排列数板子,典得不能再典的问题,这都能放来当比赛题的?
设 \(n = \sum C_i\),通常使用的公式为
但是模数不是质数,不一定存在乘法逆元,所以不能使用带除法的式子。使用另一个公式就好了:
用杨辉三角递推式可以在 \(O(n^2)\) 时间内预处理组合数,且不需要除法,于是就做完了。
参考代码
(但是赛时忘了第二个公式,想了半天)
F - Inserting Process
看到 \(N\) 很小,容易想到把每个子序列设为一个状态,这样状态数为 \(2^{N}\)。正着加字符是困难的,因为我们难以快速判定,一个子序列插入一个字符得到的字符串是不是原串的子序列。套路性地倒着考虑,即研究有多少种方法可以把原串删为空串。
一个简单的想法是:用下标序列 \((1, 2, \cdots, n)\) 的子序列 \((p_1, p_2, \cdots, p_m)\) 表示子序列 \(t = s_{p_1}s_{p_2}\cdots s_{p_m}\),枚举删去的字符来转移。问题在于:一个字符串删去不同位置的字符,可能得到相同的字符串,例如 \(\tt{aab}\) 删去第一个字符或第二个字符得到的字符串都是 \(\tt{ab}\),这样就算多了。
于是我们要解决的问题是:如何使子序列的表示方式唯一?例如对于 \(s = \tt{aab}\),我们希望子序列 \(t = \tt{ab}\) 只有一种表示方式。实际上,在转移的过程中,简单地加上一条限制即可:如果出现连续相同的字符,只能删除最右边的字符。那么,\(\tt{aab}\) 只能删除第二个字符得到 \(\tt{ab}\),这样两个子序列之间的转移就唯一了,因而解决了算重的问题。
另外多说一句,我们确实给每个子序列找到了唯一的表示方式:如果一个子序列的表示方式不唯一,我们总是取字典序最小的那个。例如 \(\tt{ab}\) 在 \(\tt{aab}\) 中的表示方式有两个:\((1, 3)\) 和 \((2, 3)\),而我们只会转移到字典序较小的 \((1, 3)\)。这不难感性理解,严谨证明可能需要使用一下数学归纳法,但我们最好不要陷入抽象的形式化语言中。
总结:对于有重复贡献的问题,考虑给每个对象确定一个唯一的表示方式。这种思想是相当常见的。
参考代码