题解 luogu.P2303 [SDOI2012] Longge 的问题

题目

luogu.P2303 [SDOI2012] Longge 的问题

比较套路化的推式子,但是需要理解具体的过程。故而写一篇题解,具体分析一下。

题意建模

给定一个整数 \(n\),你需要求出 \(\sum\limits_{i=1}^n \gcd(i, n)\),其中 \(\gcd(i, n)\) 表示 \(i\)\(n\) 的最大公因数。

  • 对于 $60% $ 的数据,保证 \(n\leq 2^{16}\)
  • 对于 $100% $ 的数据,保证 \(1\leq n< 2^{32}\)

算法分析

部分分解法

暴力,直接照着题意模拟即可。

正解

给定整数 $ n $,洛谷题目 P2303 要求计算表达式 \(\sum_{i=1}^n \gcd(i, n)\) 的值。以下是推导该公式的详细过程:

  1. 枚举约数重构表达式
    由于 \(\gcd(i, n)\) 的值一定是 $ n$ 的约数,可以将原式改写为枚举约数 $ d $ 的形式:

    \( \sum_{d \mid n} d \cdot \left| \{ i : \gcd(i, n) = d \} \right| \)

    其中 \(d\) 遍历 \(n\) 的所有正约数,内部部分表示满足 \(\gcd(i, n) = d\) 的整数 $ i $ 的数量。

  2. 转化为欧拉函数
    \(\gcd(i, n) = d\),则等价于 \(\gcd(i/d, n/d) = 1\),且 \(i/d\) 和 $ n/d $ 均为整数。因此,满足条件的 $ i $ 的数量等于不超过 $ n/d $ 且与 $n/d $ 互质的数的个数,即欧拉函数 \(\phi(n/d)\)

    \( \sum_{d \mid n} d \cdot \phi(n/d) \)

    此形式已可直接用于计算(例如枚举 $ n $ 的约数并求值)。

总结推导核心:原始求和 \(\sum \gcd(i, n)\) 被转化为约数求和形式 \(\sum_{d \mid n} d \cdot \phi(n/d)\),适用于编程求解。

所以现在就可以预处理出来质数(线性筛),然后试除法求欧拉函数。时间复杂度:
\(O(\text{质数的个数} \times \sqrt n\ )\),足以通过本题。

参考代码

暴力(70pts)

#include<iostream>
using namespace std;
int gcd(int x,int y) { return y?gcd(y,x%y):x; }
int n;
int main()
{
	cin>>n;
	long long ans=0;
	for(int i=1;i<=n;i++) 
		ans+=gcd(i,n);
	cout<<ans<<endl;
	return 0;
}

正解

#include<iostream>
#include<cstring>
#include<cmath>
#define rei register int 
using namespace std;
const int N=(1<<16)+5;
int pri[N],idx;
bool is_pri[N];
void get_pri(int n)
{
	memset(is_pri,true,sizeof(is_pri));
	is_pri[0]=is_pri[1]=false;
	for(rei i=2;i<=n;i++)
	{
		if(is_pri[i]) pri[++idx]=i;
		for(rei j=1;i*pri[j]<=n;j++)
		{
			is_pri[i*pri[j]]=false;
			if(i%pri[j]==0) break;
		}
	}
}

long long get_phi(long long n)
{
	long long ans=n;
	for(rei i=2;i<=n/i;i++)
		if(n%i==0)
		{
			ans=ans/i*(i-1);
			while(n%i==0) n/=i;
		}
	if(n>1) ans=ans/n*(n-1);
	return ans;
}

int main()
{
	long long n; cin>>n;
	get_pri(ceil(sqrt(n)));
	long long ans=0;
	for(rei i=1;i<=n/i;i++)
		if(n%i==0)
		{
			ans+=i*get_phi(n/i);		
			if(i*i!=n) ans+=n/i*get_phi(i);
		}
	cout<<ans<<endl;
	return 0;
}

细节实现

这个题,\(n\) 也要开long long。这是一个极易错的点。

总结归纳

在解有关 \(\gcd()\) 的问题时,可能会转化成欧拉函数的形式。同时我们前面探讨过,有一个贡献观点,这个时候,往往数据范围异常地大这个时候就是利用上述的观点转化。还是要多练!!这种类型的题目不在少数,所以可以通过多做培养经验。继续努力!!!

posted @ 2025-08-06 16:43  枯骨崖烟  阅读(15)  评论(0)    收藏  举报