答案是:旋转i步对应置换f_i:x→(x+i)%n,其循环个数为gcd(n,i),故不动点数为c^gcd(n,i),最终答案为(1/n)×Σc^gcd(n,i)(i=0至n−1)。

直接结论:用 Pólya 定理把旋转操作建模为循环位移置换群,对每个旋转步长 i 计算其循环节数 gcd(n, i),再套公式 ans = (1/n) * Σ c^gcd(n,i)(c 为颜色数)。
怎么把“旋转”转成置换群里的元素
环上 n 个位置编号为 0 到 n-1,顺时针旋转 i 步,对应置换 f_i:每个位置 x 映射到 (x + i) % n。这个置换可分解为若干不相交的循环,循环个数就是 gcd(n, i)。例如 n=6, i=2:
0→2→4→0、1→3→5→1 → 共 gcd(6,2)=2 个循环,每个长度为 3。
关键点:
- 所有旋转构成的群大小是 n(即 i = 0, 1, ..., n-1)
- 不必枚举全部 n 个 i,可按 d = gcd(n, i) 分组,每组内 i 的个数等于欧拉函数 φ(n/d)
- 这在 n 较大(如 1e9)时必须优化,否则会超时
为什么不动点数是 c^循环个数
一个着色在旋转 f_i 下不变,当且仅当每个循环内所有位置颜色相同。因为循环内位置通过重复旋转互相抵达,颜色必须一致才能“看起来没变”。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
所以:
- 若 f_i 有 k 个循环,则每个循环独立选一种颜色,共 c^k 种不动着色
- 对应 i 的贡献就是 c^gcd(n,i)
- 注意:这里假设颜色无数量限制;若有限制(如每种颜色至多用 m 次),就不能直接幂运算,得换 DP 或生成函数
C++ 实现时最容易错的三个地方
- gcd(0, n) 要特判:旋转 0 步即恒等置换,循环个数是 n,不是 0;标准 std::gcd 对 gcd(0,n) 返回 n,但手写时容易漏
- 整数溢出:当 n 大、c ≥2 时,c^gcd(n,i) 可能远超 long long;需用模意义下的快速幂,且注意题目是否要求取模(如 POJ 2409 要求 mod 1000000007)
- 枚举 i 的范围错误:只枚举 i ∈ [0, n-1] 是对的,但若用分组法(按约数枚举),必须确保每个 d | n 对应的 i 个数算准——即满足 gcd(n,i) == d 的 i 个数是 φ(n/d),不是 φ(d)
复杂点始终在“怎么高效枚举所有旋转对应的循环结构”,而不是定理本身。实际编码中,90% 的 bug 出现在约数分解或欧拉函数实现上,而不是公式理解。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










