题目信息
题目来源:CCF 山东省选 2012;
在线评测地址:Luogu#2303;
运行限制:时间不超过 (3.00 extrm{s}),空间不超过 (128 extrm{MiB})。
题目背景
Longge 的数学成绩非常好,并且他非常乐于挑战高难度的数学问题。
题目描述
现在问题来了:给定一个整数 (n),你需要求出 (sumlimits_{i=1}^n gcd(i,n)),其中 (gcd(i,n)) 表示 (i) 和 (n) 的最大公因数。
输入格式
输入只有一行一个整数,表示 (n)。
输出格式
输出一行一个整数表示答案。
数据规模与约定
- 对于 (60\%) 的数据,保证 (nle 2^{16})。
- 对于 (100\%) 的数据,保证 (1le nle 2^{32})。
分析
这道题太经典了,以至于后面的很多数论题都是模仿这个形式的。
(60 exttt{pts})
暴力求即可,复杂度 (mathcal{O}(nlog n))。
(100 exttt{pts})
我们要推一下式子,来优化这个算法。
枚举所有因数,计算即可,复杂度 (mathcal{O}(sigma(n)sqrt{n})),可以 AC。
Bonus
当然,这道题这么数学,其实有更好的做法,可以做到 (mathcal{O}(sqrt{n})) 的优秀复杂度。
具体内容可以看这一篇文章。
注意事项
这道题非常细节,很容易不知不觉就 WA、TLE。
边界问题
看到那个数据范围 (le 2^{32}) 没有?(n) 是可以等于 (2^{32}) 的,所以别忘了开 long long
。
当然所有中间变量都得开 long long
,否则会出现溢出。
同时,判定质数时注意平方数情况。
特判问题
求 (varphi(n)) 时,得先判质数,然后进行质因数分解。
同时,质因数分解时循环也只能开到 (sqrt{n}) 级别,最后还要特判是否有没有除掉的数。否则一个 (2 imes (2^{31}-1)) 就能卡得飞起。
还有,你有没有把 (1) 判为质数呢?特判一定要加!
Code
经过了好几次提交以后,这道题终于 (color{GreenYellow}{ exttt{AC}}) 了。
#include <cstdio>
using namespace std;
typedef long long ll;
ll phi(ll x) // 计算
{
ll ans = 1;
bool flag = false;
for (ll i = 2; i * i <= x; i++) // 判质数
if (!(x % i))
{
flag = true;
break;
}
if (!flag && x != 1) // 是质数
return x - 1; // 退出
for (ll i = 2; i * i <= x; i++) // 否则就开始找质因数
if (!(x % i)) // 找到了
{
ans *= (i - 1), x /= i; // 计算贡献
while (!(x % i))
x /= i, ans *= i;
}
if (x != 1) // 还没算干净,一定是质数
ans *= x - 1; // 直接计算贡献
return ans; // 返回答案
}
int main()
{
ll n, ans = 0;
scanf("%lld", &n); // 输入
for (ll i = 1; i * i <= n; i++) // 筛因数
if (!(n % i)) // 找到了(一对)
{
ans += phi(i) * (n / i); // 计算一个
if (i * i < n) // 不是平方数
ans += phi(n / i) * i; // 就再计算一个。
}
printf("%lld
", ans); // 输出
return 0; // 然后就 AC 了、
}