【SPOJ5971】LCMSUM

【问题描述】

    求sigma(lcm(i,n)),1 <= i <= n。

【分析】

    跟求sigma(gcd(i,n)) 有点像(POJ2480 Longge's problem),都是枚举n的约数。

    首先有个性质,在1到n中与n互质的数都是成对出现的,且他们的和为n * phi(n) / 2

    例如30,在1到n中与30互质的数有1,7,11,13,17,19,23,29.其中1 + 29= 30,7 + 23 = 30,11 + 19 = 30,13+17 =30。(1是例外,虽然2也是例外,phi(2) = 1,但是结果是一样的)

    所以,当gcd(i,n) = k,sigma(i) = n / k * phi(n / k) / 2 * k。

    lcm(i,n) = i * n / k, sigma(lcm(i,n)) = sigma(n * n/ k * phi(n/k) /  2 * k/ k) = n * sigma(n/ k * phi(n/k) / 2)

    接着只要枚举n的约数累加就行了,虽然慢但是还是能A的。

    这道题时限卡得比较紧,所以不仅筛法,求phi都要O(N),求约数要注意时间,而且也要注意细节。记得用long long,不然会挂掉,也不能乱用long long,不然可能会TLE。

【代码】

    

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
int n,pt =0,t;
long long ans;
bool p[1000005];
long long prime[500000],phi[1000005],fact[4000],num[4000];
void Init()
{
    phi[1] = 1;
    for (int i = 2;i <= 1000000;i ++)
    {
        if (!p[i]) 
            pt ++, prime[pt] = i,phi[i] = i - 1;
        for (int j = 1;j <= pt && (long long)i * prime[j] <= 1000000;j ++)
        {
            p[prime[j] * i] = true;
            if (i % prime[j] == 0)
            {
                    phi[i * prime[j]] = prime[j] * phi[i];
                break;
            }
                else phi[i * prime[j]] = (prime[j] - 1) * phi[i];
        }
     } 
}
void dfs(int i,long long now)
{
    if (i == t + 1)
    {
        //cout << now << ' '<< phi[now] << endl;
        ans += now * phi[now] / 2;
        return;
    }
    dfs(i + 1,now);
    for (int j = 1;j <= num[i];j ++)
    {
        now *= fact[i];
        dfs(i + 1,now);
    }
}
void div(long long n)
{
    t = 0;
    for (int i = 1;i <= pt;i ++)
    {
        if (prime[i] * prime[i] > n || !p[n]) break;
        if (n % prime[i]) continue;
        fact[++t] = prime[i];
        num[t] = 0;
        while (n % prime[i] == 0) n /= prime[i],num[t] ++;
    }
    if (n > 1 && !p[n])
        fact[++t] = n,num[t] = 1;
}
int main()
{
    int Tcase;
    scanf("%d",&Tcase);
    Init();
    while (Tcase--)
    {
        scanf("%d",&n);
        ans = 0;
        div(n);
        dfs(1,1);
        printf("%lld\n",(long long)(ans + 1) * n); // +1是为了处理第一个性质的1
    }
}

 

posted @ 2013-01-20 20:58  N_C_Derek  阅读(350)  评论(0编辑  收藏  举报