spoj 5971. LCM Sum
Problem code: LCMSUM
Given n, calculate the sum LCM(1,n) + LCM(2,n) + .. + LCM(n,n), where LCM(i,n) denotes the Least Common Multiple of the integers i and n.
Input
The first line contains T the number of test cases. Each of the next T lines contain an integer n.
Output
Output T lines, one for each test case, containing the required sum.
Example
Sample Input :
3
1
2
5
Sample Output :
1
4
55
Constraints
1 <= T <= 300000
1 <= n <= 1000000
/*
spoj 5971 LCM SUM
http://www.spoj.pl/problems/LCMSUM/
sigma lcm( i, n ) = sigma ( i * n / gcd( i, n ) ) = n * sigma ( i / gcd( i, n ) )
设 gcd( i, n ) = k, i = k * j, n = k * m ( j <= m ) ,所以
只要枚举n的每个因数,对于每个因数q,求出小于等于q,且与q互素的数的和,将所有的和加起来就是答案了
(1)对于小于等于q,且与q互素的数的和,有公式 q * phi( p ) / 2,具体证明如下:
小于N且与N互质的正整数之和, 设为S.
不妨设这些数为a[1], a[2], ..., a[ phi(N) ], 其中phi(N)是N的欧拉函数值.
对1 <= i <= phi(N), 由gcd( N, a[i] ) = 1可知gcd( N, N - a[i] ) = 1.
这里可以采用反证: 设gcd( N, N - a[i] ) = k > 1,
则 k | N, k | ( N - a[i] ) -> k | a[i] -> k | gcd( N, a[i] ), 而gcd( N , a[i] ) = 1, 矛盾.
这样, N - a[1], N - a[2], ..., N - a[ phi(N) ]也对应着原数列, 则有:
S = a[1] + a[2] + ... + a[ phi(N) ]
S = (N - a[1]) + (N - a[2]) + ... + (N - a[ phi[N] ])
两式相加得: S = N * phi(N) / 2.
(2)这题n比较小,可以先预处理1到n的所欧拉函数。
(3)1是特殊情况,我的程序会少算一个1,所以最后加回去了。
*/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1010;
int prime[maxn],vis[maxn],pl;
void _prime( )//求素数
{
for(int i=2;i<maxn;i++)
{
if(vis[i]==0) prime[pl++]=i;
for(int j=0;j<pl&&i*prime[j]<maxn;j++)
{
vis[i*prime[j]]=1;
if(i%prime[j]==0) break;
}
}
}
int phi[1000100];
void phi_table(int n)//筛选法求欧拉函数
{
for(int i=2 ; i<= n; i++) phi[i] = 0;
phi[1]= 1;
for(int i =2; i<=n ;i ++)
if(!phi[i])
for(int j = i ; j<=n; j+=i)
{
if(!phi[j]) phi[j] = j;
phi[j] = phi[j] /i*(i-1);
}
}
int fac[1000],num[1000],fl;//分解因数,fl表示因数个数
long long ans;
void dfs(int id,long long cnt)//枚举n的所有因子 (cnt)
{
if(id==fl)
{
ans+=cnt*phi[cnt]/2;
return ;
}
long long temp=1;
for(int i=0;i<=num[id];i++)
{
dfs(id+1,cnt*temp);
temp*=fac[id];
}
}
int main()
{
phi_table(1000100);//求欧拉函数值
_prime();//筛选法求素数
int ci;scanf("%d",&ci);
while(ci--)
{
int n;scanf("%d",&n);
int b=n;//分解质因数
fl=0;memset(num,0,sizeof(num));
for(int i=0;i<pl&&prime[i]*prime[i]<=b;i++)//因为数据不会超,所以直接乘
{
if(b%prime[i]==0)
{
for(fac[fl]=prime[i];b%prime[i]==0;num[fl]++,b/=prime[i]);
fl++;
}
}
if(b>1) num[fl]=1,fac[fl++]=b;
ans=0;
dfs(0,1);
printf("%lld/n",(ans+1)*n);//少算了一个1,最后要加上
}
return 0;
}