[解题报告]烷基计数&烯烃计数&烷烃计数
Foreword
昨天晚上讲题的时候看到 \(\mathrm{M\color{red}{\_sea}}\) 巨巨在写烷基计数, 正好我之前在搞 Burnside 引理, 于是也找来做了.
由于不清楚难度顺序, 就先开了「烷烃计数」, 想试着往之前有色图那个做法上套, 结果想了一个多小时一点思路都没有. (不过话说从这个数据范围应该就看得出做法明显不一样吧…)
后来 \(\mathrm{M\color{red}{\_sea}}\) 给我指了条明路, 让我先去做「烷基计数」. 一语点醒梦中人, 于是就有了这篇解题报告.
(三倍经验警告)
烷基计数
Statement
Solution
「烷基计数」比「烷烃计数」简单的地方在于, 「烷基计数」是有根的, 那么我们就可以考虑从根开始往下递归.
首先, 考虑怎么处理根的儿子的同构情况. 这里我们直接使用 Burnside 引理, 便把无标号的子节点变成有标号了, 然后再对每个置换算出它所对应的染色方案数量即可 (即不动点方案). 因为这里根只有 \(3\) 个子节点 (子节点为空的话就看做该子树的大小为 \(0\)), 所以总共只有 \(3! = 6\) 中置换, 手动把每种置换的不动点数量算出来即可.
具体来说, 设 \(f_i\) 表示大小为 \(i\) 的烷基的同分异构体的数量, 则有
\[ f_i = \frac{1}{6} (2[3 \mid (i - 1)]f_{\frac{i - 1}{3}} + 3\sum_{j = 0}^{\lfloor \frac{i - 1}{2} \rfloor} f_jf_{i - 1 - 2j} + \sum_{j = 0}^{i - 1} \sum_{k = 0}^{i - 1 - j} f_jf_kf_{i - 1 - j - k}) \]
这样的话就可以得到一个 \(O(n^3)\) 的暴力.
下面考虑使用生成函数优化.
(由于本人才疏学浅并且对生成函数的应用极其不熟练, 所以以下内容借鉴于这篇博客).
(怎么想都想不到可以把指数放在 x 上)
设 \(F(x) = \sum_{i \ge 0} f_ix^i\). 那么上面的式子可以写成
\[ F(x) = 1 + \frac{1}{6}x[2F(x^3) + 3F(x^2)F(x) + F(x)^3] \]
(这里不要像我一样看到 \(F(x^3)\) 和 \(F(x^2)\) 这两个东西懵逼了, 实际上把它们看做两个另外的函数就可以了.)
这个式子可以分治FFT, 也可以牛顿迭代. 分治 FFT 的做法比较显然, 下面大致描述一下牛顿迭代的做法.
首先设 \(G(F(x)) = 1 + \frac{1}{6}x[2F(x^3) + 3F(x^2)F(x) + F(x)^3] - F(x)\).
然后按照牛顿迭代的套路, 假设已经求出了 \(F_0(x)\) 满足 \(G(F_0(X)) \equiv 0 \pmod{x^{\frac{n}{2}}}\), 现在要求 \(F(x)\) 满足 \(G(F(x)) \equiv 0 \pmod{x^n}\)
\[ F(x) = F_0(x) - \frac{G(F_0(x))}{G'(F_0(x))} \]
手动把 \(G'(F_0(x))\) 化一下即可. 实现的时候可以分子分母同乘一个常数, 就可以不用乘那么多逆元了.
时间复杂度为 \(O(n\log n)\).
实现的时候注意清空会导致循环卷积的位置.
Code
|
|
烯烃计数
statement
Solution
容易想到把碳碳双键看做根, 而碳碳双键两边的碳原子所连出去的东西其实就是若干个烷基, 所以先按照上面的方法算出烷基个数.
然后碳碳双键两边的碳原子只有两个儿子, 那么也与上面类似, 对大小为 \(2\) 的置换讨论一下, 列出式子, 然后生成函数乘一下即可. (需要注意这里 \(x^0\) 的系数要为 \(0\), 因为碳碳双键两边必须要接碳原子.)
然后对于这个碳碳双键, 可以把它看做一个点, 然后也是用 Burnside 引理按照上面的做法弄一下就好了.
时间复杂度为 \(O(n \log n)\).
(其实和烷基计数没什么区别, 不过可以用来水水题量.)
Code
|
|
烷烃计数
Statement
Solution.
在经历了「烷基计数」和「烯烃计数」的洗礼后, 我突然领悟了 \(\mathrm{M\color{red}{\_sea}}\) 看似漫不经心实际别有深意地 说出的一个词: 「重心」.
现在, 烷烃计数难搞的地方只在于它没有根, 那么我们要是能对一个烷烃找到一个唯一的根, 那么问题就迎刃而解了.
而「重心」就是一个经典的选择.
对于 \(n\) 为奇数的情况, 由于重心只会有一个, 所以直接把重心作为根即可.
具体来说, 就是先求出烷基个数, 在最后合并的时候, 根节点有 \(4\) 个儿子, 并且每一个子树的大小都小于 \(\lceil \frac{n}{2} \rceil \). 那么我们对 \(4! = 24\) 个置换讨论一下即可. 当然可能会有点麻烦.
对于 \(n\) 为偶数的情况, 若重心有一个子树的大小为 \( \frac{n}{2} \), 则这时会有两个重心, 这个烷烃就会被统计两次. (可以画个图理解.)
所以我们限制每个子树大小都小于 \( \frac{n}{2} \), 然后按上面 \(n\) 为奇数的情况统计.
而对于有两个重心的情况, 我们可以把两个重心之间那条边看做一个点, 然后对 \(2! = 2\) 个置换讨论一下即可. (跟上面烯烃计数的情况差不多.)
复杂度为 \(O(n \log n)\).
Code
|
|