题目描述
for i=1 to n
for j=1 to n
sum+=gcd(i,j)
给出n求sum. gcd(x,y)表示x,y的最大公约数.
输入输出格式
输入格式:
n
输出格式:
sum
输入输出样例
输入样例#1:
2
输出样例#1:
5
说明
数据范围 30% n<=3000 60% 7000<=n<=7100 100% n<=100000
题目十分奇特,我们知道1e6的数据n^2 log n 卡不死你也能卡爆你。我们需要将复杂度控制在O(n log n)以内。我们这样想,首先这些GCD可以被重新分类:GCD值为同一个数倍数的为一类,有什么好处呢?我们发现对于每一个i,那么gcd为i的倍数的个数就是(n/i)^2 (/表示整除),那么这一类的和就是(n/i)^2 * i,这样我们只需要进行容斥统计每一个i就可以了。想到了倍数,那么我们就可以考虑这样一个问题:类似莫比乌斯函数,我们是不是也可以用类似的方法推断出在一定前缀之内,每一个i类的数应该被计算多少次呢?于是我们可以先用一层递推推出每一个i类数对应该计算多少次。那么最后的答案就变成了sum = sig(f[i] * (n/i) ^ 2)。就可以O(n log n)预处理O(n)查询了。但是其实我们能更优,做到根号n的复杂度。具体看代码:
#include<cstdio>
using namespace std;
const int maxn = 1e5 + 5;
long long f[maxn];
int n;
int pre(int n) {
for(register int i = 1; i <= n; ++i){
for(register int j = i * 2; j <= n; j += i)
f[j] += i - f[i];
f[i] = i - f[i];
f[i] += f[i - 1];
}
}
int main() {
scanf("%d", &n);
pre(n);
int dis = 1;
long long sig = 0;
for(register int i = 1; i <= n; i = dis + 1) {
dis = (n / (n / i));
sig += (f[dis] - f[i - 1]) * (n / i) * (n / i);
}
printf("%lld\n", sig);
return 0;
}
rockdu
没有帐号? 立即注册