天天看點

bzoj 2226: [Spoj 5971] LCMSum 數學+歐拉函數題意分析代碼

題意

求 ∑ni=1lcm(i,n)

T<=300000

n<=1000000

分析

ans=∑ni=1lcm(i,n)

=∑ni=1i∗ngcd(i,n)

=∑d|n∑gcd(i,nd)=11<=i<=ndn∗i

=n∗∑d|n∑gcd(i,nd)=11<=i<=ndi

設F(d)=∑gcd(i,d)=11<=i<=di

則ans=n∗∑d|nF(d)

有一個結論就是F(d)=d∗ϕ(d)2

直接線篩預處理歐拉函數然後每次暴力求即可。

代碼

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 1000005
#define LL long long
using namespace std;

int tot,prime[N],phi[N];
bool not_prime[N];

void get_prime(int n)
{
    phi[]=;
    for (int i=;i<=n;i++)
    {
        if (!not_prime[i]) prime[++tot]=i,phi[i]=i-;
        for (int j=;j<=tot&&i*prime[j]<=n;j++)
        {
            not_prime[i*prime[j]]=;
            if (i%prime[j]==)
            {
                phi[i*prime[j]]=phi[i]*prime[j];
                break;
            }
            phi[i*prime[j]]=phi[i]*(prime[j]-);
        }
    }
}

LL solve(int n)
{
    int w=sqrt(n);LL ans=+(LL)n*phi[n]/;
    for (int i=;i<=w;i++)
        if (n%i==)
        {
            ans+=(LL)i*phi[i]/;
            if (n/i!=i) ans+=(LL)n/i*phi[n/i]/;
        }
    return ans*n;
}

int main()
{
    get_prime();
    int T;
    scanf("%d",&T);
    while (T--)
    {
        int n;
        scanf("%d",&n);
        printf("%lld\n",solve(n));
    }
    return ;
}