1 条题解
-
0
题目分析
题目要求计算 (n) 重求和 (\sum_{i_1=1}^{m}\sum_{i_2=1}^{m} \dots \sum_{i_n=1}^{m}\gcd(i_1,i_2,\dots,i_n)),其中 (n,m) 可达 (10^{11})。直接枚举显然不可行,需通过数论变换(莫比乌斯反演或欧拉函数)结合整除分块、杜教筛优化求解。
解题思路
- 数论变换:利用 (\gcd(i_1,\dots,i_n) = \sum_{d|i_1,\dots,d|i_n} \phi(d))(欧拉函数的性质),将求和转化为对 (d) 的枚举: [ \sum_{d=1}^m \phi(d) \cdot \left\lfloor \frac{m}{d} \right\rfloor^n ]
- 预处理与杜教筛:预处理小范围欧拉函数前缀和,大范围使用杜教筛递归计算。
- 整除分块:对 (\left\lfloor \frac{m}{d} \right\rfloor) 相同的 (d) 分块,减少计算量。
- 快速幂:计算 (\left\lfloor \frac{m}{d} \right\rfloor^n) 时使用快速幂,时间复杂度 (O(\log n))。
代码实现(带注释)
#include <bits/stdc++.h> #define int unsigned long long // 使用unsigned long long避免溢出 using namespace std; const int M = 1e7; // 预处理欧拉函数的上限 unordered_map <int, int> mp; // 杜教筛用的哈希表,存储已计算的前缀和 int phi[M + 10], pri[M], c = 0; // phi数组存储欧拉函数值,pri存储质数,c是质数计数 bitset < M + 10 > is; // 筛法用的布尔数组,标记是否为合数 // 快速幂:计算a^k,对2^64取模(自然溢出) int Pow(int a, int k) { int ans = 1; for (; k; k >>= 1, a = a * a) // 二进制分解指数k if (k & 1) ans = ans * a; return ans; } // 预处理欧拉函数及前缀和(埃氏筛+线性筛) void init() { phi[1] = 1; // 1的欧拉函数值为1 for (int i = 2; i <= M; i++) { if (!is[i]) { // i是质数 pri[++c] = i; phi[i] = i - 1; // 质数的欧拉函数值为自身减一 } for (int j = 1; j <= c && pri[j]*i <= M; j++) { is[i * pri[j]] = 1; // 标记为合数 if (i % pri[j] == 0) { // pri[j]是i的质因子 phi[i * pri[j]] = phi[i] * pri[j]; // 欧拉函数性质 break; } phi[i * pri[j]] = phi[i] * phi[pri[j]]; // 互质时欧拉函数乘积 } } // 计算欧拉函数前缀和 for (int i = 2; i <= M; i++) phi[i] += phi[i - 1]; } // 杜教筛:计算欧拉函数前缀和sum_{i=1}^n phi(i) int sum_phi(int n) { if (n <= M) // 小范围直接返回预处理结果 return phi[n]; if (mp.count(n)) // 已计算过,直接返回哈希表中的值 return mp[n]; // 初始值:sum_{i=1}^n i = n*(n+1)/2 int ans; if (n & 1) ans = (n + 1) / 2 * n; else ans = n / 2 * (n + 1); // 整除分块递归计算 for (int l = 2, r; l <= n; l = r + 1) { r = n / (n / l); // 相同n/l的右端点 ans -= (r - l + 1) * sum_phi(n / l); // 减去多余部分 } return mp[n] = ans; // 存入哈希表并返回 } int n, k, ans = 0; // n是重数,k是m,ans存储最终答案 signed main() { ios::sync_with_stdio(0); // 加速输入输出 cin.tie(0), cout.tie(0); init(); // 预处理欧拉函数 cin >> n >> k; // 整除分块计算答案 for (int l = 1, r; l <= k; l = r + 1) { r = k / (k / l); // 相同k/l的右端点 // 累加 phi[r]-phi[l-1] 乘以 (k/l)^n ans = (ans + (sum_phi(r) - sum_phi(l - 1)) * Pow(k / l, n)); } cout << ans; // 输出答案(自然溢出即为对2^64取模) return 0; }代码解释
- 预处理欧拉函数:使用线性筛法预处理 (1) 到 (10^7) 的欧拉函数值及前缀和,时间复杂度 (O(M))。
- 杜教筛:递归计算大范围欧拉函数前缀和,通过整除分块减少递归次数,时间复杂度 (O(n^{2/3}))。
- 整除分块:对 (d) 按 (\left\lfloor \frac{m}{d} \right\rfloor) 分块,每块内的 (\left\lfloor \frac{m}{d} \right\rfloor^n) 相同,结合快速幂计算,时间复杂度 (O(\sqrt{m} \log n))。
- 自然溢出:利用
unsigned long long的自然溢出特性实现对 (2^{64}) 取模,无需额外取模操作。
- 1
信息
- ID
- 5651
- 时间
- 2000ms
- 内存
- 512MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者