洛谷P2398 GCD SUM
? 解题记录 ? ? 洛谷 ? ? 筛法 ?    2017-09-14 12:00:40    251    0    0

题目描述

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;
}

 

上一篇: UVa 11426 GCD Extreme(II)

下一篇: 51Nod 1154 回文串划分

251 人读过
立即登录, 发表评论.
没有帐号? 立即注册
0 条评论
文档导航