POJ 2480 Longge's problen(欧拉函数)

Longge's problem

Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 9220 Accepted: 3085

Description

Longge is good at mathematics and he likes to think about hard mathematical problems which will be solved by some graceful algorithms. Now a problem comes: Given an integer N(1 < N < 2^31),you are to calculate ∑gcd(i, N) 1<=i <=N. 

"Oh, I know, I know!" Longge shouts! But do you know? Please solve it. 

Input

Input contain several test case. 
A number N per line. 

Output

For each N, output ,∑gcd(i, N) 1<=i <=N, a line

Sample Input

2
6

Sample Output

3
15

Source

POJ Contest,Author:Mathematica@ZSU

【思路】

要求\sum_{i = 1}^{N} gcd(i, N) * i。设某个i与N的最大公约数为p,则有N = p × m,i = p × n,且n <= m,n与m互质。显然与N的最大公约数为p的i的个数就是n的个数,也就是\varphi (m)。所以枚举N的所有因数搞一搞就是答案了。我们把N分解一下,它的质因数不会太多,然后组合出N的所有因数就好了(我将分解后的结果放在map里,暴力组合时为此还开发出迭代器作为递归函数参数的新写法,后来看别人的题解发现这种做法其实很智障,没必要分解,只需要将1到\sqrt{N}遍历一下考察数对就能得到所有因数了,读者看我的代码就当看新奇好了)。

 

【代码】

//******************************************************************************
// File Name: POJ_2480.cpp
// Author: Shili_Xu
// E-Mail: shili_xu@qq.com
// Created Time: 2018年07月29日 星期日 09时26分22秒
//******************************************************************************

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;
typedef long long ll;

int n;
ll ans;
map<int, int> p;

int phi(int x)
{
	int ans = x;
	for (int i = 2; (ll)i * i <= x; i++) {
		if (x % i == 0) {
			ans /= i, ans *= (i - 1);
			while (x % i == 0) x /= i;
		}
	}
	if (x > 1) ans /= x, ans *= (x - 1);
	return ans;
}

void get_factor(int x)
{
	p.clear();
	for (int i = 2; (ll)i * i <= x; i++) {
		while (x % i == 0) x /= i, (!p.count(i) ? p[i] = 1 : p[i]++);
	}
	if (x > 1) p[x]++;
}

void dfs(map<int, int>::iterator it, int now)
{
	if (it == p.end()) {
		int m = n / now;
		ans += (ll)phi(m) * now;
		return;
	}
	map<int, int>::iterator a = it;
	for (int i = 0; i <= it->second; i++) dfs(++it, now), it = a, now *= it->first;
}

int main()
{
	while (scanf("%d", &n) == 1) {
		get_factor(n);
		ans = 0;
		dfs(p.begin(), 1);
		printf("%lld\n", ans);
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值