Longge's problem
Time Limit:1000MS Memory Limit:65536K
Total Submit:1156 Accepted:308
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
好的,开始分析。
首先看什么是欧拉函数:
关于形式为的函数,在数论中,对正整数n,欧拉函数φ(n)是小于或等于n的正整数中与n互质的数的数目。
显然,对于素数p,φ(p)= p -1.对于两个素数p、q,他们的乘积n = pq 满足φ(n) =(p-1)(q-1)。下面证明:
证明:对于质数p,q,满足φ(n) =(p-1)(q-1)
考虑n的完全余数集Zn = { 1,2,....,pq -1}
而不和n互质的集合由下面三个集合的并构成:
1) 能够被p整除的集合{p,2p,3p,....,(q-1)p} 共计q-1个
2) 能够被q整除的集合{q,2q,3q,....,(p-1)q} 共计p-1个
3) {0}
很显然,1、2集合中没有共同的元素,因此Zn中元素个数 = pq - (p-1 + q- 1 + 1) = (p-1)(q-1)
由此应该可以理解欧拉函数的积性性质:只要gcd(m,n)=1,则必有φ(m*n)=φ(m)*φ(n)。
理解一个句子:φ(N/pi)就是1~N中所有满足gcd(s,N)=pi的s的个数。仔细体会。
再理解一个句子,不难理解:一个正整数总可以表示成素数(它的质因子)的乘积: N = p1^k1 * p2^k2 * ... * pn^kn (这里p1,p2,..pn是素数,当然相互之间是互质的)
再理解一个性质,做好准备:φ(pi^k)=pi*\(pi^(k-1))=pi^(k-1)*φ(pi)=(pi-1)*pi^(k-1),pi为质数。
回到题目,设F(N)=∑gcd(i, N) ,1<=i<=N.
下面考虑一个命题:如果 N 可由 N=P^k 表示,(为什么要用P^k表示后面就知道了) P为素数,那么:
F[N]=k×(P^k-P^(k-1))+P^k
有了上面的基础,该命题的证明就不算困难了:
证明: 由 F[N]=∑gcd(i, N) 得 F(N)=∑pi*φ(N/pi), pi|N, 其中 pi 取值为 1 , P^1 , P^2 , P^3..., P^(k-1), P^k
欧拉函数φ(N/pi)其实就是1-N中所有满足gcd(m,N)=pi的m的个数 (这点很重要)
根据欧拉函数的性质:若 a=b^k, b为素数, 则φ(a)=(b-1)* b^(k-1)
所以对于 F(N)=∑pi*φ(N/pi) = 1*φ( P^k ) + P*φ( P^(k-1) ) + P^2 *φ( P^(k-2) ) +...P^(k-1)*φ(P) + P^k *φ(1)
F(N) = (P-1)*( P^(k-1) ) + (P-1)*(P)*( P^(k-2) ) + (P-1)* (P^2)* ( P^(k-3) ) +...+ (P-1)* ( P^(k-1) ) + P^k
可以看出 F(N)= k * (P-1)*( P^(k-1) ) + P^k = k×(P^k-P^(k-1))+P^k ,得证.
回忆前文,一个正整数总可以表示成素数的乘积:
N = p1^k1
所以F(N)=F[p1^k1] * F[p2^k2] * ... * F[pn^kn]
= (k1×(p1^k1-p1^(k1-1))+p1^k1) * (k2×(p2^k2-p2^(k2-1))+p2^k2) * ... * (kn×(pn^kn-pn^(kn-1))+pn^kn)
分析至此,对数进行分解质因数的分析即可计算得到答案。
#include<cstdio>
int main ()
{
long long n,i,phi;
while(scanf("%lld",&n)!=EOF)
{
phi=1;
for(i=2;i*i<=n;++i)
{
if(n%i!=0)
continue;
long long p=0,t=1;
while(n%i==0)
{
n=n/i;
p++;
t*=i;
}
phi*=p*(t-t/i)+t; //F[P^k]=k×(P^k-P^(k-1))+P^k (P为质数)
}
if(n!=1)
phi*=2*n-1;
printf("%lld\n",phi);
}
return 0;
}