1 条题解
-
0
题目分析
问题描述: 给定字符串长度 和字符集大小 ,求有多少个长度为 的字符串,其满足:该字符串或者是回文串,或者其某个循环移位是回文串(即“循环回文串”)。答案对 取模。
数据范围: ,,模数 可能是大质数。
算法核心思路
这道题的本质是运用 Burnside引理/Polya定理 的思想配合 容斥原理 来计数,同时因为 很大,需要使用 Pollard's Rho 算法进行质因数分解。
1. 数学推导
一个字符串 被称为循环回文串,当且仅当它属于某个“回文项链”。 我们需要计算长度为 的循环回文串的数量。这可以通过枚举循环节长度(即 的因子 )来解决。
根据相关数学结论(基于 Burnside 引理的变体),答案的形式涉及到对 的所有因子进行遍历求和。代码中
$$\text{Ans} = \sum_{d | n} \left( K^{\lceil d/2 \rceil} \times \text{val}(d) \times \text{coeff}(n/d) \right) $$dfs函数实际上是在计算如下公式(经过化简):其中:
- :当前枚举的因子(代码中的
p)。 - :长度为 的回文串数量。代码中的
g(p)函数计算的就是这个值。- 如果 是偶数,回文串前半部分确定后半部分,数量为 。
- 如果 是奇数,中间一位也是自由的,数量为 。
- :对于长度 的回文串,其产生的不同循环移位数量的贡献因子。
- 代码中体现为
(p & 1 ? p : p >> 1)。 - 若 为奇数,贡献为 。
- 若 为偶数,贡献为 。这是因为偶数长度的回文串在循环移位视角下存在对称性导致的重复(例如
abba循环移位只有 2 种本质不同的回文形态,或者在项链计数中的特殊权重)。
- 代码中体现为
- :这是一个容斥系数,用于处理非本原(primitive)串的重复计数问题。
- 在代码的
dfs中,变量t维护了这个系数。 - 对于 的每一个质因子 ,系数乘以 。
- 如果 包含某质因子 的次数与 相同(即 不含因子 ),则系数乘以 。
- 这实际上是在计算一种类似于欧拉函数 的变体,用于筛选出以 为最小循环节的贡献。
- 在代码的
2. 代码实现关键点
由于 高达 ,直接 或 枚举都是不可行的。
-
大数分解 (Pollard's Rho):
- 为了获取 的因子,代码使用了 Miller-Rabin 素性测试 (
MR) 和 Pollard's Rho 启发式分解算法 (PR)。 init函数将 分解为 ,存储在数组a(质数) 和b(指数) 中。
- 为了获取 的因子,代码使用了 Miller-Rabin 素性测试 (
-
DFS 枚举因子:
- 利用分解后的质因数,通过
dfs递归生成 的所有因子 。 - 相比直接遍历 ,直接利用质因数生成因子的效率极高(对于 内的数,因子个数通常不多)。
- 利用分解后的质因数,通过
-
光速幂 (分块预处理):
- 在计算 时,指数可能很大。普通的快速幂
ksm是 的。 - 代码使用了 分块预处理 策略(类似于 Baby-step Giant-step 的思想)来优化幂运算。
mi0,mi1,mi2,mi3分别预处理了 的 , 等范围的幂。- 这样可以将单次幂运算的时间复杂度从 降至 ,极大地加速了 DFS 过程中的计算。
- 在计算 时,指数可能很大。普通的快速幂
-
__int128:- 由于模数 接近 或更大,中间乘法运算会爆
long long,因此使用了__int128(i7) 来保证取模前的精度。
- 由于模数 接近 或更大,中间乘法运算会爆
题解总结
步骤:
- 读入 。
- 使用 Pollard's Rho 算法对 进行质因数分解。
- 预处理 的幂次表,将 64 位整数拆分为 4 个 16 位的段,实现 查询 。
- 使用 DFS 根据质因数搜索 的所有因子 。
- 在搜索过程中,根据公式 $\text{Ans} += g(p) \times \text{val}(p) \times \text{coeff}(n/p)$ 累加答案。
- 其中系数
t的更新逻辑为:若当前质因子未选满(即 含有该质因子),则t *= (1 - prime);否则t *= 1。
- 其中系数
- 输出最终累加的答案。
复杂度分析:
- 分解质因数: 期望复杂度。
- DFS:复杂度取决于 的因子个数,对于 范围内的数,因子个数上限很少(约 量级),配合 的幂运算,完全可以在时限内通过。
这份代码是解决此类大数数论计数问题的标准且高效的模板。
- :当前枚举的因子(代码中的
- 1
信息
- ID
- 6136
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者