C++ 单位根反演(Roots of Unity Filter)全解析
C++ 单位根反演(Roots of Unity Filter)全解析
前言
单位根反演是组合数学、数论、生成函数、多项式算法中最核心的等价变换公式,专门解决整除性判定、模 k 计数、下标取模筛选、周期性求和问题。它能把「对下标 i ≡ r (mod k) 求和」这种带取模限制的求和,转化为无限制普通求和,从而大幅简化计算。
在算法竞赛(ACM/NOI/CSP)、密码学、信号处理、C++ 高性能数学库、NTT 相关算法里,单位根反演是必备工具。它的本质是:用复数/模意义下单位根,构造“筛选器”,精准提取满足同余条件的项。
本文从数学定义→公式推导→几何意义→经典题型→C++ 完整实现全覆盖,理论严谨、代码可直接运行。
目录
- 单位根基础(复数域 + 模素数域)
- 单位根反演公式核心推导
- 公式直观理解:筛选器原理
- 最常用 3 大模板公式
- 典型应用场景(取模求和、整除计数、循环卷积)
- C++ 完整实现(复数版 + 模素数 NTT 版)
- 例题解析与代码运行演示
- 复杂度与工程优化
一、前置知识:n 次单位根
1.1 复数域单位根
n 次单位根:满足 ( z^n = 1 ) 的复数。
主 n 次单位根:
[
\omega_n = e^{\frac{2\pi i}{n}} = \cos\frac{2\pi}{n} + i\sin\frac{2\pi}{n}
]
核心性质:
- ( \omega_n^n = 1 )
- ( \omega_n^k ) 两两不同
- 正交性(反演基础):
[
\frac{1}{n}\sum_{j=0}^{n-1} \omega_n^{j\cdot k} = \begin{cases} 1 & n\mid k \ 0 & else \end{cases}
]
1.2 模素数域单位根(算法中最常用)
算法里不用复数,用模素数域,通过原根构造单位根:
设模数 ( P ) 是质数,( g ) 是原根,则:
[
\omega_n = g^{\frac{P-1}{n}}
]
要求:n | (P-1)。
常用模数:
- 998244353(原根3)
- 1004535809(原根3)
二、单位根反演核心公式(最重要)
2.1 基础公式(正交性)
[
\boxed{\frac{1}{k} \sum_{j=0}^{k-1} \omega_k^{j\cdot t} = [k \mid t]}
]
含义:
- 若 t 是 k 的倍数 → 结果 = 1
- 否则 → 结果 = 0
这就是整除判定器。
2.2 反演公式(求和筛选)
我们要计算:
[
S = \sum_{n} [k \mid n] \cdot a_n
]
代入单位根反演:
[
S = \frac{1}{k} \sum_{j=0}^{k-1} \sum_{n} a_n \cdot \omega_k^{jn}
]
交换求和顺序:
[
S = \frac{1}{k} \sum_{j=0}^{k-1} F(\omega_k^j)
]
其中 ( F(x) = \sum a_n x^n ) 是生成函数。
一句话总结:单位根反演 = 把取模限制,变成对单位根的求值。
2.3 更一般:同余 n ≡ r (mod k)
[
\sum_{n \equiv r \pmod k} a_n
= \frac{1}{k}\sum_{j=0}^{k-1} \omega_k^{-jr} F(\omega_k^j)
]
这是算法竞赛最常用模板。
三、直观理解:单位根 = 筛选器
你可以把单位根反演看作:
- 构造 k 个“频率”
- 只有下标是 k 倍数的项会共振保留
- 其余项全部抵消为 0
它就是数学里的带通滤波器。
四、单位根反演三大标准模板(背会直接用)
模板 1:求下标被 k 整除的和
[
\sum_{k\mid n} a_n
= \frac1k \sum_{j=0}^{k-1} F(\omega_k^j)
]
模板 2:求下标 ≡ r (mod k) 的和
[
\sum_{n\equiv r\pmod k} a_n
= \frac1k \sum_{j=0}^{k-1} \omega_k^{-jr} F(\omega_k^j)
]
模板 3:计数 f(n) 中满足 n mod k = r 的数量
[
ans = \frac1k \sum_{j=0}^{k-1} \omega_k^{-jr} \sum_n f(n)\omega_k^{jn}
]
五、典型应用场景
- 求 n! 中指数是 k 的倍数的项和
- 斐波那契数列中满足下标 ≡0/1/2 mod k 的和
- 组合数取模筛选
- 循环卷积、周期序列求和
- 生成函数限定下标提取
- NTT 加速的取模类 DP 优化
凡是题目出现:
“mod k”、“被 k 整除”、“下标同余”、“周期为 k”
99% 可以用单位根反演。
六、C++ 完整实现
版本 1:复数版(教学、直观)
#include <iostream>
#include <vector>
#include <complex>
#include <cmath>
using namespace std;
typedef complex<double> cd;
const double PI = acos(-1);
// 主 n 次单位根
cd omega(int n, int k) {
return exp(cd(0, 2 * PI * k / n));
}
// 单位根反演:求 a_n 中 n ≡ r (mod k) 的和
template<typename Func>
double roots_of_unity_filter(Func&& F, int k, int r) {
cd res = 0;
for (int j = 0; j < k; j++) {
cd w = omega(k, j);
cd coeff = exp(cd(0, -2 * PI * j * r / k)); // w^(-jr)
res += coeff * F(w);
}
return res.real() / k;
}
// 示例:F(x) = 1 + x + x^2 + x^3 + x^4 + x^5
double test_F(cd x) {
return 1.0 + x + x*x + x*x*x + x*x*x*x + x*x*x*x*x;
}
int main() {
int k = 3, r = 0;
double ans = roots_of_unity_filter(test_F, k, r);
// 下标 0,3 → 1+1=2
cout << "n ≡ 0 mod 3 的和 = " << ans << endl;
return 0;
}
版本 2:模素数版(算法竞赛正式版)
真正比赛/工程用这个
#include <iostream>
#include <vector>
using namespace std;
const int MOD = 998244353;
const int G = 3;
long long qpow(long long a, long long b) {
long long res = 1;
while (b) {
if (b & 1) res = res * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return res;
}
// 求 k 次单位根
long long get_omega(int k) {
return qpow(G, (MOD - 1) / k);
}
// 单位根反演求 sum [n ≡ r mod k] a_n
long long solve(const vector<long long>& a, int k, int r) {
long long w = get_omega(k);
long long inv_k = qpow(k, MOD - 2);
long long ans = 0;
for (int j = 0; j < k; j++) {
long long wj = qpow(w, j);
long long c = qpow(wj, (k - r) % k); // w^(-jr)
long long F = 0;
long long p = 1;
for (auto v : a) {
F = (F + v * p) % MOD;
p = p * wj % MOD;
}
ans = (ans + c * F) % MOD;
}
ans = ans * inv_k % MOD;
return ans;
}
int main() {
vector<long long> a = {1,1,1,1,1,1}; // 1+x+x^2+x^3+x^4+x^5
int k = 3, r = 0;
cout << solve(a, k, r) << endl; // 输出 2
return 0;
}
七、公式推导(最关键的一步)
我们要证明:
[
\frac1k\sum_{j=0}{k-1}\omega_k{jn} = [k\mid n]
]
证明:
设 ( n = qk + t )
若 ( t=0 ):
[
\frac1k\sum_{j=0}^{k-1} 1 = 1
]
若 ( t≠0 ):
[
\frac1k \cdot \frac{\omega_k{kn}-1}{\omega_kn-1} = \frac1k \cdot 0 = 0
]
证毕。
八、复杂度分析
- 暴力筛选:( O(n) )
- 单位根反演:( O(k \cdot M) )
- M 是生成函数求值代价
- 若配合 NTT:可做到 ( O(n\log n) )
k 通常很小(如 2,3,4,5),速度极快。
九、适用条件
- 必须存在 k 次单位根
- 模意义下要求 ( k \mid (P-1) )
- 生成函数可快速求值
- 问题包含同余、整除、周期
十、总结(最核心 3 句话)
- 单位根反演 = 用单位根构造整除筛选器
- 核心公式:
[
\frac1k\sum_{j=0}{k-1}\omega_k{jn} = [k\mid n]
] - 凡是题目出现 mod k、被 k 整除、下标同余,直接套单位根反演。
更多推荐

所有评论(0)